Remove InstanceMixin auto-url magicks.

This commit is contained in:
Tom Christie 2012-02-23 09:21:01 +00:00
parent 2b59df004a
commit e15494a172
10 changed files with 35 additions and 88 deletions

View File

@ -25,14 +25,13 @@ __all__ = (
'ResponseMixin', 'ResponseMixin',
'AuthMixin', 'AuthMixin',
'ResourceMixin', 'ResourceMixin',
# Reverse URL lookup behavior
'InstanceMixin',
# Model behavior mixins # Model behavior mixins
'ReadModelMixin', 'ReadModelMixin',
'CreateModelMixin', 'CreateModelMixin',
'UpdateModelMixin', 'UpdateModelMixin',
'DeleteModelMixin', 'DeleteModelMixin',
'ListModelMixin' 'ListModelMixin',
'PaginatorMixin'
) )
@ -444,30 +443,6 @@ class ResourceMixin(object):
else: else:
return None return None
##########
class InstanceMixin(object):
"""
`Mixin` class that is used to identify a `View` class as being the canonical identifier
for the resources it is mapped to.
"""
@classmethod
def as_view(cls, **initkwargs):
"""
Store the callable object on the resource class that has been associated with this view.
"""
view = super(InstanceMixin, cls).as_view(**initkwargs)
resource = getattr(cls(**initkwargs), 'resource', None)
if resource:
# We do a little dance when we store the view callable...
# we need to store it wrapped in a 1-tuple, so that inspect will treat it
# as a function when we later look it up (rather than turning it into a method).
# This makes sure our URL reversing works ok.
resource.view_callable = (view,)
return view
########## Model Mixins ########## ########## Model Mixins ##########
@ -599,7 +574,7 @@ class CreateModelMixin(ModelMixin):
manager.through(**data).save() manager.through(**data).save()
headers = {} headers = {}
if hasattr(instance, 'get_absolute_url'): if hasattr(self.resource, 'url'):
headers['Location'] = self.resource(self).url(instance) headers['Location'] = self.resource(self).url(instance)
return Response(status.HTTP_201_CREATED, instance, headers) return Response(status.HTTP_201_CREATED, instance, headers)

View File

@ -1,10 +1,7 @@
from django import forms from django import forms
from django.core.urlresolvers import get_urlconf, get_resolver, NoReverseMatch
from django.db import models
from djangorestframework.response import ErrorResponse from djangorestframework.response import ErrorResponse
from djangorestframework.reverse import reverse from djangorestframework.serializer import Serializer
from djangorestframework.serializer import Serializer, _SkipField
from djangorestframework.utils import as_tuple from djangorestframework.utils import as_tuple
@ -20,7 +17,7 @@ class BaseResource(Serializer):
def __init__(self, view=None, depth=None, stack=[], **kwargs): def __init__(self, view=None, depth=None, stack=[], **kwargs):
super(BaseResource, self).__init__(depth, stack, **kwargs) super(BaseResource, self).__init__(depth, stack, **kwargs)
self.view = view self.view = view
self.request = view.request self.request = getattr(view, 'request', None)
def validate_request(self, data, files=None): def validate_request(self, data, files=None):
""" """
@ -224,9 +221,6 @@ class ModelResource(FormResource):
Also provides a :meth:`get_bound_form` method which may be used by some renderers. Also provides a :meth:`get_bound_form` method which may be used by some renderers.
""" """
# Auto-register new ModelResource classes into _model_to_resource
#__metaclass__ = _RegisterModelResource
form = None form = None
""" """
The form class that should be used for request validation. The form class that should be used for request validation.
@ -260,7 +254,7 @@ class ModelResource(FormResource):
The list of fields to exclude. This is only used if :attr:`fields` is not set. The list of fields to exclude. This is only used if :attr:`fields` is not set.
""" """
include = ('url',) include = ()
""" """
The list of extra fields to include. This is only used if :attr:`fields` is not set. The list of extra fields to include. This is only used if :attr:`fields` is not set.
""" """
@ -323,49 +317,6 @@ class ModelResource(FormResource):
return form() return form()
def url(self, instance):
"""
Attempts to reverse resolve the url of the given model *instance* for this resource.
Requires a ``View`` with :class:`mixins.InstanceMixin` to have been created for this resource.
This method can be overridden if you need to set the resource url reversing explicitly.
"""
if not hasattr(self, 'view_callable'):
raise _SkipField
# dis does teh magicks...
urlconf = get_urlconf()
resolver = get_resolver(urlconf)
possibilities = resolver.reverse_dict.getlist(self.view_callable[0])
for tuple_item in possibilities:
possibility = tuple_item[0]
# pattern = tuple_item[1]
# Note: defaults = tuple_item[2] for django >= 1.3
for result, params in possibility:
#instance_attrs = dict([ (param, getattr(instance, param)) for param in params if hasattr(instance, param) ])
instance_attrs = {}
for param in params:
if not hasattr(instance, param):
continue
attr = getattr(instance, param)
if isinstance(attr, models.Model):
instance_attrs[param] = attr.pk
else:
instance_attrs[param] = attr
try:
return reverse(self.view_callable[0],
kwargs=instance_attrs,
request=self.view.request)
except NoReverseMatch:
pass
raise _SkipField
@property @property
def _model_fields_set(self): def _model_fields_set(self):
""" """

View File

@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse as django_reverse
from django.utils.functional import lazy from django.utils.functional import lazy
def reverse(viewname, request, *args, **kwargs): def reverse(viewname, *args, **kwargs):
""" """
Same as `django.core.urlresolvers.reverse`, but optionally takes a request Same as `django.core.urlresolvers.reverse`, but optionally takes a request
and returns a fully qualified URL, using the request to get the base URL. and returns a fully qualified URL, using the request to get the base URL.

View File

@ -1,5 +1,4 @@
from django.conf.urls.defaults import patterns, url from django.conf.urls.defaults import patterns, url
from django.test import TestCase
from django.forms import ModelForm from django.forms import ModelForm
from django.contrib.auth.models import Group, User from django.contrib.auth.models import Group, User
from djangorestframework.resources import ModelResource from djangorestframework.resources import ModelResource
@ -7,18 +6,22 @@ from djangorestframework.views import ListOrCreateModelView, InstanceModelView
from djangorestframework.tests.models import CustomUser from djangorestframework.tests.models import CustomUser
from djangorestframework.tests.testcases import TestModelsTestCase from djangorestframework.tests.testcases import TestModelsTestCase
class GroupResource(ModelResource): class GroupResource(ModelResource):
model = Group model = Group
class UserForm(ModelForm): class UserForm(ModelForm):
class Meta: class Meta:
model = User model = User
exclude = ('last_login', 'date_joined') exclude = ('last_login', 'date_joined')
class UserResource(ModelResource): class UserResource(ModelResource):
model = User model = User
form = UserForm form = UserForm
class CustomUserResource(ModelResource): class CustomUserResource(ModelResource):
model = CustomUser model = CustomUser

View File

@ -6,7 +6,6 @@ By setting or modifying class attributes on your view, you change it's predefine
""" """
import re import re
from django.core.urlresolvers import set_script_prefix, get_script_prefix
from django.http import HttpResponse from django.http import HttpResponse
from django.utils.html import escape from django.utils.html import escape
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -269,7 +268,7 @@ class ModelView(View):
resource = resources.ModelResource resource = resources.ModelResource
class InstanceModelView(InstanceMixin, ReadModelMixin, UpdateModelMixin, DeleteModelMixin, ModelView): class InstanceModelView(ReadModelMixin, UpdateModelMixin, DeleteModelMixin, ModelView):
""" """
A view which provides default operations for read/update/delete against a model instance. A view which provides default operations for read/update/delete against a model instance.
""" """

View File

@ -2,6 +2,7 @@ from django.db import models
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify
import uuid import uuid
def uuid_str(): def uuid_str():
return str(uuid.uuid1()) return str(uuid.uuid1())
@ -14,6 +15,7 @@ RATING_CHOICES = ((0, 'Awful'),
MAX_POSTS = 10 MAX_POSTS = 10
class BlogPost(models.Model): class BlogPost(models.Model):
key = models.CharField(primary_key=True, max_length=64, default=uuid_str, editable=False) key = models.CharField(primary_key=True, max_length=64, default=uuid_str, editable=False)
title = models.CharField(max_length=128) title = models.CharField(max_length=128)
@ -37,4 +39,3 @@ class Comment(models.Model):
comment = models.TextField() comment = models.TextField()
rating = models.IntegerField(blank=True, null=True, choices=RATING_CHOICES, help_text='How did you rate this post?') rating = models.IntegerField(blank=True, null=True, choices=RATING_CHOICES, help_text='How did you rate this post?')
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)

View File

@ -11,6 +11,11 @@ class BlogPostResource(ModelResource):
fields = ('created', 'title', 'slug', 'content', 'url', 'comments') fields = ('created', 'title', 'slug', 'content', 'url', 'comments')
ordering = ('-created',) ordering = ('-created',)
def url(self, instance):
return reverse('blog-post',
kwargs={'key': instance.key},
request=self.request)
def comments(self, instance): def comments(self, instance):
return reverse('comments', return reverse('comments',
kwargs={'blogpost': instance.key}, kwargs={'blogpost': instance.key},

View File

@ -1,4 +1,5 @@
from djangorestframework.resources import ModelResource from djangorestframework.resources import ModelResource
from djangorestframework.reverse import reverse
from modelresourceexample.models import MyModel from modelresourceexample.models import MyModel
@ -6,3 +7,8 @@ class MyModelResource(ModelResource):
model = MyModel model = MyModel
fields = ('foo', 'bar', 'baz', 'url') fields = ('foo', 'bar', 'baz', 'url')
ordering = ('created',) ordering = ('created',)
def url(self, instance):
return reverse('model-resource-instance',
kwargs={'id': instance.id},
request=self.request)

View File

@ -2,7 +2,10 @@ from django.conf.urls.defaults import patterns, url
from djangorestframework.views import ListOrCreateModelView, InstanceModelView from djangorestframework.views import ListOrCreateModelView, InstanceModelView
from modelresourceexample.resources import MyModelResource from modelresourceexample.resources import MyModelResource
my_model_list = ListOrCreateModelView.as_view(resource=MyModelResource)
my_model_instance = InstanceModelView.as_view(resource=MyModelResource)
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^$', ListOrCreateModelView.as_view(resource=MyModelResource), name='model-resource-root'), url(r'^$', my_model_list, name='model-resource-root'),
url(r'^(?P<id>[0-9]+)/$', InstanceModelView.as_view(resource=MyModelResource), name='model-resource-instance'), url(r'^(?P<id>[0-9]+)/$', my_model_instance, name='model-resource-instance'),
) )

View File

@ -15,7 +15,11 @@ class ExampleView(View):
""" """
Handle GET requests, returning a list of URLs pointing to 3 other views. Handle GET requests, returning a list of URLs pointing to 3 other views.
""" """
return {"Some other resources": [reverse('another-example', kwargs={'num':num}, request=request) for num in range(3)]} resource_urls = [reverse('another-example',
kwargs={'num': num},
request=request)
for num in range(3)]
return {"Some other resources": resource_urls}
class AnotherExampleView(View): class AnotherExampleView(View):