Allow related models to be fully serialized

This commit is contained in:
Tom Christie 2011-06-15 14:09:01 +01:00
parent 412b5fc2d5
commit 7dcb851c7f
15 changed files with 195 additions and 237 deletions

View File

@ -479,6 +479,7 @@ class ReadModelMixin(object):
""" """
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
model = self.resource.model model = self.resource.model
try: try:
if args: if args:
# If we have any none kwargs then assume the last represents the primrary key # If we have any none kwargs then assume the last represents the primrary key
@ -498,6 +499,7 @@ class CreateModelMixin(object):
""" """
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
model = self.resource.model model = self.resource.model
# translated 'related_field' kwargs into 'related_field_id' # translated 'related_field' kwargs into 'related_field_id'
for related_name in [field.name for field in model._meta.fields if isinstance(field, RelatedField)]: for related_name in [field.name for field in model._meta.fields if isinstance(field, RelatedField)]:
if kwargs.has_key(related_name): if kwargs.has_key(related_name):
@ -522,6 +524,7 @@ class UpdateModelMixin(object):
""" """
def put(self, request, *args, **kwargs): def put(self, request, *args, **kwargs):
model = self.resource.model model = self.resource.model
# TODO: update on the url of a non-existing resource url doesn't work correctly at the moment - will end up with a new url # TODO: update on the url of a non-existing resource url doesn't work correctly at the moment - will end up with a new url
try: try:
if args: if args:
@ -547,6 +550,7 @@ class DeleteModelMixin(object):
""" """
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):
model = self.resource.model model = self.resource.model
try: try:
if args: if args:
# If we have any none kwargs then assume the last represents the primrary key # If we have any none kwargs then assume the last represents the primrary key
@ -581,10 +585,12 @@ class ListModelMixin(object):
queryset = None queryset = None
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
queryset = self.queryset if self.queryset else self.resource.model.objects.all() model = self.resource.model
queryset = self.queryset if self.queryset else model.objects.all()
if hasattr(self, 'resource'): if hasattr(self, 'resource'):
ordering = getattr(self.resource.Meta, 'ordering', None) ordering = getattr(self.resource, 'ordering', None)
else: else:
ordering = None ordering = None

View File

@ -181,7 +181,7 @@ class DocumentingTemplateRenderer(BaseRenderer):
# Get the form instance if we have one bound to the input # Get the form instance if we have one bound to the input
form_instance = None form_instance = None
if method == view.method.lower(): if method == getattr(view, 'method', view.request.method).lower():
form_instance = getattr(view, 'bound_form_instance', None) form_instance = getattr(view, 'bound_form_instance', None)
if not form_instance and hasattr(view, 'get_bound_form'): if not form_instance and hasattr(view, 'get_bound_form'):

View File

@ -20,12 +20,11 @@ class BaseResource(Serializer):
""" """
Base class for all Resource classes, which simply defines the interface they provide. Base class for all Resource classes, which simply defines the interface they provide.
""" """
class Meta: fields = None
fields = None include = None
include = None exclude = None
exclude = None
def __init__(self, view, 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
@ -49,20 +48,19 @@ class Resource(BaseResource):
Objects that a resource can act on include plain Python object instances, Django Models, and Django QuerySets. Objects that a resource can act on include plain Python object instances, Django Models, and Django QuerySets.
""" """
class Meta: # The model attribute refers to the Django Model which this Resource maps to.
# The model attribute refers to the Django Model which this Resource maps to. # (The Model's class, rather than an instance of the Model)
# (The Model's class, rather than an instance of the Model) model = None
model = None
# By default the set of returned fields will be the set of: # By default the set of returned fields will be the set of:
# #
# 0. All the fields on the model, excluding 'id'. # 0. All the fields on the model, excluding 'id'.
# 1. All the properties on the model. # 1. All the properties on the model.
# 2. The absolute_url of the model, if a get_absolute_url method exists for the model. # 2. The absolute_url of the model, if a get_absolute_url method exists for the model.
# #
# If you wish to override this behaviour, # If you wish to override this behaviour,
# you should explicitly set the fields attribute on your class. # you should explicitly set the fields attribute on your class.
fields = None fields = None
class FormResource(Resource): class FormResource(Resource):
@ -74,12 +72,11 @@ class FormResource(Resource):
view, which may be used by some renderers. view, which may be used by some renderers.
""" """
class Meta: """
""" The :class:`Form` class that should be used for request validation.
The :class:`Form` class that should be used for request validation. This can be overridden by a :attr:`form` attribute on the :class:`views.View`.
This can be overridden by a :attr:`form` attribute on the :class:`views.View`. """
""" form = None
form = None
def validate_request(self, data, files=None): def validate_request(self, data, files=None):
@ -189,7 +186,7 @@ class FormResource(Resource):
""" """
# A form on the view overrides a form on the resource. # A form on the view overrides a form on the resource.
form = getattr(self.view, 'form', None) or self.Meta.form form = getattr(self.view, 'form', None) or self.form
# Use the requested method or determine the request method # Use the requested method or determine the request method
if method is None and hasattr(self.view, 'request') and hasattr(self.view, 'method'): if method is None and hasattr(self.view, 'request') and hasattr(self.view, 'method'):
@ -235,44 +232,43 @@ class ModelResource(FormResource):
# Auto-register new ModelResource classes into _model_to_resource # Auto-register new ModelResource classes into _model_to_resource
#__metaclass__ = _RegisterModelResource #__metaclass__ = _RegisterModelResource
class Meta: """
""" The form class that should be used for request validation.
The form class that should be used for request validation. If set to :const:`None` then the default model form validation will be used.
If set to :const:`None` then the default model form validation will be used.
This can be overridden by a :attr:`form` attribute on the :class:`views.View`. This can be overridden by a :attr:`form` attribute on the :class:`views.View`.
""" """
form = None form = None
""" """
The model class which this resource maps to. The model class which this resource maps to.
This can be overridden by a :attr:`model` attribute on the :class:`views.View`. This can be overridden by a :attr:`model` attribute on the :class:`views.View`.
""" """
model = None model = None
""" """
The list of fields to use on the output. The list of fields to use on the output.
May be any of: May be any of:
The name of a model field. The name of a model field.
The name of an attribute on the model. The name of an attribute on the model.
The name of an attribute on the resource. The name of an attribute on the resource.
The name of a method on the model, with a signature like ``func(self)``. The name of a method on the model, with a signature like ``func(self)``.
The name of a method on the resource, with a signature like ``func(self, instance)``. The name of a method on the resource, with a signature like ``func(self, instance)``.
""" """
fields = None fields = None
""" """
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.
""" """
exclude = ('id', 'pk') exclude = ('id', 'pk')
""" """
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.
""" """
include = ('url',) include = ('url',)
def __init__(self, view): def __init__(self, view):
@ -283,7 +279,7 @@ class ModelResource(FormResource):
""" """
super(ModelResource, self).__init__(view) super(ModelResource, self).__init__(view)
self.model = getattr(view, 'model', None) or self.Meta.model self.model = getattr(view, 'model', None) or self.model
def validate_request(self, data, files=None): def validate_request(self, data, files=None):
@ -399,7 +395,7 @@ class ModelResource(FormResource):
isinstance(getattr(self.model, attr, None), property) isinstance(getattr(self.model, attr, None), property)
and not attr.startswith('_')) and not attr.startswith('_'))
if self.Meta.fields: if self.fields:
return property_fields & set(as_tuple(self.Meta.fields)) return property_fields & set(as_tuple(self.fields))
return property_fields.union(set(as_tuple(self.Meta.include))) - set(as_tuple(self.Meta.exclude)) return property_fields.union(set(as_tuple(self.include))) - set(as_tuple(self.exclude))

View File

@ -46,30 +46,14 @@ class _SkipField(Exception):
pass pass
class _BuildInnerMeta(type): class _RegisterSerializer(type):
""" """
Some magic so that an inner Meta class gets inheriting behavior. Metaclass to register serializers.
""" """
def __new__(cls, name, bases, attrs): def __new__(cls, name, bases, attrs):
# Get a list of all the inner Metas, from the bases upwards.
inner_metas = [getattr(base, 'Meta', object) for base in bases]
inner_metas.append(attrs.get('Meta', object))
# Build up the attributes on the inner Meta.
meta_attrs = {}
[meta_attrs.update(inner_meta.__dict__) for inner_meta in inner_metas]
# Drop private attributes.
meta_attrs = dict([ (key, val) for (key, val) in meta_attrs.items()
if not key.startswith('_') ])
# Lovely, now we can create our inner Meta class.
attrs['Meta'] = type('Meta', (object,), meta_attrs)
# Build the class and register it. # Build the class and register it.
ret = super(_BuildInnerMeta, cls).__new__(cls, name, bases, attrs) ret = super(_RegisterSerializer, cls).__new__(cls, name, bases, attrs)
_serializers[name] = ret _serializers[name] = ret
return ret return ret
@ -78,8 +62,8 @@ class Serializer(object):
Converts python objects into plain old native types suitable for Converts python objects into plain old native types suitable for
serialization. In particular it handles models and querysets. serialization. In particular it handles models and querysets.
The output format is specified by setting a number of attributes on the The output format is specified by setting a number of attributes
inner `Meta` class. on the class.
You may also override any of the serialization methods, to provide You may also override any of the serialization methods, to provide
for more flexible behavior. for more flexible behavior.
@ -87,75 +71,61 @@ class Serializer(object):
Valid output types include anything that may be directly rendered into Valid output types include anything that may be directly rendered into
json, xml etc... json, xml etc...
""" """
__metaclass__ = _BuildInnerMeta __metaclass__ = _RegisterSerializer
class Meta: fields = ()
""" """
Information on how to serialize the data. Specify the fields to be serialized on a model or dict.
""" Overrides `include` and `exclude`.
"""
fields = () include = ()
""" """
Specify the fields to be serialized on a model or dict. Fields to add to the default set to be serialized on a model/dict.
Overrides `include` and `exclude`. """
"""
include = () exclude = ()
""" """
Fields to add to the default set to be serialized on a model/dict. Fields to remove from the default set to be serialized on a model/dict.
""" """
exclude = () rename = {}
""" """
Fields to remove from the default set to be serialized on a model/dict. A dict of key->name to use for the field keys.
""" """
rename = {} related_serializer = None
""" """
A dict of key->name to use for the field keys. The default serializer class to use for any related models.
""" """
related_serializer = None depth = None
""" """
The default serializer class to use for any related models. The maximum depth to serialize to, or `None`.
""" """
depth = None
"""
The maximum depth to serialize to, or `None`.
"""
def __init__(self, depth=None, stack=[], **kwargs): def __init__(self, depth=None, stack=[], **kwargs):
""" self.depth = depth or self.depth
Allow `Meta` items to be set on init.
"""
self.depth = depth
self.stack = stack self.stack = stack
if self.depth is None:
self.depth = self.Meta.depth
for (key, val) in kwargs.items():
setattr(self.Meta, key, val)
def get_fields(self, obj): def get_fields(self, obj):
""" """
Return the set of field names/keys to use for a model instance/dict. Return the set of field names/keys to use for a model instance/dict.
""" """
fields = self.Meta.fields fields = self.fields
# If Meta.fields is not set, we use the default fields and modify # If `fields` is not set, we use the default fields and modify
# them with Meta.include and Meta.exclude # them with `include` and `exclude`
if not fields: if not fields:
default = self.get_default_fields(obj) default = self.get_default_fields(obj)
include = self.Meta.include or () include = self.include or ()
exclude = self.Meta.exclude or () exclude = self.exclude or ()
fields = set(default + list(include)) - set(exclude) fields = set(default + list(include)) - set(exclude)
else: else:
fields = _fields_to_list(self.Meta.fields) fields = _fields_to_list(self.fields)
return fields return fields
@ -163,7 +133,7 @@ class Serializer(object):
def get_default_fields(self, obj): def get_default_fields(self, obj):
""" """
Return the default list of field names/keys for a model instance/dict. Return the default list of field names/keys for a model instance/dict.
These are used if `Meta.fields` is not given. These are used if `fields` is not given.
""" """
if isinstance(obj, models.Model): if isinstance(obj, models.Model):
opts = obj._meta opts = obj._meta
@ -173,15 +143,14 @@ class Serializer(object):
def get_related_serializer(self, key): def get_related_serializer(self, key):
info = _fields_to_dict(self.Meta.fields).get(key, None) info = _fields_to_dict(self.fields).get(key, None)
# If an element in `fields` is a 2-tuple of (str, tuple) # If an element in `fields` is a 2-tuple of (str, tuple)
# then the second element of the tuple is the fields to # then the second element of the tuple is the fields to
# set on the related serializer # set on the related serializer
if isinstance(info, (list, tuple)): if isinstance(info, (list, tuple)):
class OnTheFlySerializer(Serializer): class OnTheFlySerializer(Serializer):
class Meta: fields = info
fields = info
return OnTheFlySerializer return OnTheFlySerializer
# If an element in `fields` is a 2-tuple of (str, Serializer) # If an element in `fields` is a 2-tuple of (str, Serializer)
@ -200,15 +169,15 @@ class Serializer(object):
return _serializers[info] return _serializers[info]
# Otherwise use `related_serializer` or fall back to `Serializer` # Otherwise use `related_serializer` or fall back to `Serializer`
return getattr(self.Meta, 'related_serializer') or Serializer return getattr(self, 'related_serializer') or Serializer
def serialize_key(self, key): def serialize_key(self, key):
""" """
Keys serialize to their string value, Keys serialize to their string value,
unless they exist in the `Meta.rename` dict. unless they exist in the `rename` dict.
""" """
return getattr(self.Meta.rename, key, key) return getattr(self.rename, key, key)
def serialize_val(self, key, obj): def serialize_val(self, key, obj):
@ -251,8 +220,7 @@ class Serializer(object):
def serialize_model(self, instance): def serialize_model(self, instance):
""" """
Given a model instance or dict, serialize it to a dict, using Given a model instance or dict, serialize it to a dict..
the given behavior given on Meta.
""" """
data = {} data = {}

View File

@ -69,12 +69,10 @@ class TestFieldNesting(TestCase):
Test tuple nesting on `fields` attr Test tuple nesting on `fields` attr
""" """
class SerializerM2(Serializer): class SerializerM2(Serializer):
class Meta: fields = (('field', ('field1',)),)
fields = (('field', ('field1',)),)
class SerializerM3(Serializer): class SerializerM3(Serializer):
class Meta: fields = (('field', ('field2',)),)
fields = (('field', ('field2',)),)
self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}}) self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}}) self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
@ -85,20 +83,16 @@ class TestFieldNesting(TestCase):
Test related model serialization Test related model serialization
""" """
class NestedM2(Serializer): class NestedM2(Serializer):
class Meta: fields = ('field1', )
fields = ('field1', )
class NestedM3(Serializer): class NestedM3(Serializer):
class Meta: fields = ('field2', )
fields = ('field2', )
class SerializerM2(Serializer): class SerializerM2(Serializer):
class Meta: fields = [('field', NestedM2)]
fields = [('field', NestedM2)]
class SerializerM3(Serializer): class SerializerM3(Serializer):
class Meta: fields = [('field', NestedM3)]
fields = [('field', NestedM3)]
self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}}) self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}}) self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
@ -108,20 +102,16 @@ class TestFieldNesting(TestCase):
Test related model serialization Test related model serialization
""" """
class SerializerM2(Serializer): class SerializerM2(Serializer):
class Meta: fields = [('field', 'NestedM2')]
fields = [('field', 'NestedM2')]
class SerializerM3(Serializer): class SerializerM3(Serializer):
class Meta: fields = [('field', 'NestedM3')]
fields = [('field', 'NestedM3')]
class NestedM2(Serializer): class NestedM2(Serializer):
class Meta: fields = ('field1', )
fields = ('field1', )
class NestedM3(Serializer): class NestedM3(Serializer):
class Meta: fields = ('field2', )
fields = ('field2', )
self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}}) self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}}) self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})

View File

@ -75,8 +75,7 @@ class TestNonFieldErrors(TestCase):
return self.cleaned_data #pragma: no cover return self.cleaned_data #pragma: no cover
class MockResource(FormResource): class MockResource(FormResource):
class Meta: form = MockForm
form = MockForm
class MockView(View): class MockView(View):
pass pass
@ -100,12 +99,10 @@ class TestFormValidation(TestCase):
qwerty = forms.CharField(required=True) qwerty = forms.CharField(required=True)
class MockFormResource(FormResource): class MockFormResource(FormResource):
class Meta: form = MockForm
form = MockForm
class MockModelResource(ModelResource): class MockModelResource(ModelResource):
class Meta: form = MockForm
form = MockForm
class MockFormView(View): class MockFormView(View):
resource = MockFormResource resource = MockFormResource
@ -278,8 +275,7 @@ class TestModelFormValidator(TestCase):
return 'read only' return 'read only'
class MockResource(ModelResource): class MockResource(ModelResource):
class Meta: model = MockModel
model = MockModel
class MockView(View): class MockView(View):
resource = MockResource resource = MockResource

View File

@ -18,9 +18,19 @@ In this example we're working from two related models:
Creating the resources Creating the resources
---------------------- ----------------------
Once we have some existing models there's very little we need to do to create the API. We need to create two resources that we map to our two existing models, in order to describe how the models should be serialized.
Firstly create a resource for each model that defines which fields we want to expose on the model. Our resource descriptions will typically go into a module called something like 'resources.py'
Secondly we map a base view and an instance view for each resource.
``resources.py``
.. include:: ../../examples/blogpost/resources.py
:literal:
Creating views for our resources
--------------------------------
Once we've created the resources there's very little we need to do to create the API.
For each resource we'll create a base view, and an instance view.
The generic views :class:`.ListOrCreateModelView` and :class:`.InstanceModelView` provide default operations for listing, creating and updating our models via the API, and also automatically provide input validation using default ModelForms for each model. The generic views :class:`.ListOrCreateModelView` and :class:`.InstanceModelView` provide default operations for listing, creating and updating our models via the API, and also automatically provide input validation using default ModelForms for each model.
``urls.py`` ``urls.py``

View File

@ -25,7 +25,14 @@ Here's the model we're working from in this example:
.. include:: ../../examples/modelresourceexample/models.py .. include:: ../../examples/modelresourceexample/models.py
:literal: :literal:
To add an API for the model, all we need to do is create a Resource for the model, and map a couple of views to it in our urlconf. To add an API for the model, first we need to create a Resource for the model.
``resources.py``
.. include:: ../../examples/modelresourceexample/resources.py
:literal:
Then we simply map a couple of views to the Resource in our urlconf.
``urls.py`` ``urls.py``

View File

@ -1,6 +1,35 @@
Alternative Frameworks Alternative frameworks & Why Django REST framework
====================== ==================================================
#. `django-piston <https://bitbucket.org/jespern/django-piston/wiki/Home>`_ is excellent, and has a great community behind it. This project is based on piston code in parts. Alternative frameworks
----------------------
#. `django-tasypie <https://github.com/toastdriven/django-tastypie>`_ is also well worth looking at. There are a number of alternative REST frameworks for Django:
* `django-piston <https://bitbucket.org/jespern/django-piston/wiki/Home>`_ is very mature, and has a large community behind it. This project was originally based on piston code in parts.
* `django-tasypie <https://github.com/toastdriven/django-tastypie>`_ is also very good, and has a very active and helpful developer community and maintainers.
* Other interesting projects include `dagny <https://github.com/zacharyvoase/dagny>`_ and `dj-webmachine <http://benoitc.github.com/dj-webmachine/>`_
Why use Django REST framework?
------------------------------
The big benefits of using Django REST framework come down to:
1. It's based on Django's class based views, which makes it simple, modular, and future-proof.
2. It stays as close as possible to Django idioms and language throughout.
3. The browse-able API makes working with the APIs extremely quick and easy.
Why was this project created?
-----------------------------
For me the browse-able API is the most important aspect of Django REST framework.
I wanted to show that Web APIs could easily be made Web browse-able,
and demonstrate how much better browse-able Web APIs are to work with.
Being able to navigate and use a Web API directly in the browser is a huge win over only having command line and programmatic
access to the API. It enables the API to be properly self-describing, and it makes it much much quicker and easier to work with.
There's no fundamental reason why the Web APIs we're creating shouldn't be able to render to HTML as well as JSON/XML/whatever,
and I really think that more Web API frameworks *in whatever language* ought to be taking a similar approach.

View File

@ -80,8 +80,7 @@ Using Django REST framework can be as simple as adding a few lines to your urlco
from myapp.models import MyModel from myapp.models import MyModel
class MyResource(ModelResource): class MyResource(ModelResource):
class Meta: model = MyModel
model = MyModel
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^$', ListOrCreateModelView.as_view(resource=MyResource)), url(r'^$', ListOrCreateModelView.as_view(resource=MyResource)),

View File

@ -1,10 +1,5 @@
:mod:`serializer` :mod:`serializer`
================= =================
.. module:: serializer .. automodule:: serializer
.. autoclass:: serializer::Serializer.Meta
:members:
.. autoclass:: serializer::Serializer
:members: :members:

View File

@ -22,6 +22,9 @@ class BlogPost(models.Model):
slug = models.SlugField(editable=False, default='') slug = models.SlugField(editable=False, default='')
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
"""
For the purposes of the sandbox, limit the maximum number of stored models.
"""
self.slug = slugify(self.title) self.slug = slugify(self.title)
super(self.__class__, self).save(*args, **kwargs) super(self.__class__, self).save(*args, **kwargs)
for obj in self.__class__.objects.order_by('-created')[MAX_POSTS:]: for obj in self.__class__.objects.order_by('-created')[MAX_POSTS:]:

View File

@ -1,38 +1,11 @@
from django.conf.urls.defaults import patterns, url from django.conf.urls.defaults import patterns, url
from django.core.urlresolvers import reverse
from djangorestframework.views import ListOrCreateModelView, InstanceModelView from djangorestframework.views import ListOrCreateModelView, InstanceModelView
from djangorestframework.resources import ModelResource from blogpost.resources import BlogPostResource, CommentResource
from blogpost.models import BlogPost, Comment
class BlogPostResource(ModelResource):
"""
A Blog Post has a *title* and *content*, and can be associated with zero or more comments.
"""
class Meta:
model = BlogPost
fields = ('created', 'title', 'slug', 'content', 'url', 'comments')
ordering = ('-created',)
def comments(self, instance):
return reverse('comments', kwargs={'blogpost': instance.key})
class CommentResource(ModelResource):
"""
A Comment is associated with a given Blog Post and has a *username* and *comment*, and optionally a *rating*.
"""
class Meta:
model = Comment
fields = ('username', 'comment', 'created', 'rating', 'url', 'blogpost')
ordering = ('-created',)
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^$', ListOrCreateModelView.as_view(resource=BlogPostResource), name='blog-posts-root'), url(r'^$', ListOrCreateModelView.as_view(resource=BlogPostResource), name='blog-posts-root'),
url(r'^(?P<key>[^/]+)/$', InstanceModelView.as_view(resource=BlogPostResource)), url(r'^(?P<key>[^/]+)/$', InstanceModelView.as_view(resource=BlogPostResource), name='blog-post'),
url(r'^(?P<blogpost>[^/]+)/comments/$', ListOrCreateModelView.as_view(resource=CommentResource), name='comments'), url(r'^(?P<blogpost>[^/]+)/comments/$', ListOrCreateModelView.as_view(resource=CommentResource), name='comments'),
url(r'^(?P<blogpost>[^/]+)/comments/(?P<id>[^/]+)/$', InstanceModelView.as_view(resource=CommentResource)), url(r'^(?P<blogpost>[^/]+)/comments/(?P<id>[^/]+)/$', InstanceModelView.as_view(resource=CommentResource)),
) )

View File

@ -1,15 +1,8 @@
from django.conf.urls.defaults import patterns, url from django.conf.urls.defaults import patterns, url
from djangorestframework.views import ListOrCreateModelView, InstanceModelView from djangorestframework.views import ListOrCreateModelView, InstanceModelView
from djangorestframework.resources import ModelResource from modelresourceexample.resources import MyModelResource
from modelresourceexample.models import MyModel
class MyModelResource(ModelResource):
class Meta:
model = MyModel
fields = ('foo', 'bar', 'baz', 'url')
ordering = ('created',)
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^$', ListOrCreateModelView.as_view(resource=MyModelResource), name='model-resource-root'), url(r'^$', ListOrCreateModelView.as_view(resource=MyModelResource), name='model-resource-root'),
url(r'^([0-9]+)/$', InstanceModelView.as_view(resource=MyModelResource)), url(r'^(?P<pk>[0-9]+)/$', InstanceModelView.as_view(resource=MyModelResource)),
) )

View File

@ -46,19 +46,12 @@ class HTMLRenderer(BaseRenderer):
media_type = 'text/html' media_type = 'text/html'
class PygmentsFormResource(FormResource):
"""
"""
form = PygmentsForm
class PygmentsRoot(View): class PygmentsRoot(View):
""" """
This example demonstrates a simple RESTful Web API aound the awesome pygments library. This example demonstrates a simple RESTful Web API around the awesome pygments library.
This top level resource is used to create highlighted code snippets, and to list all the existing code snippets. This top level resource is used to create highlighted code snippets, and to list all the existing code snippets.
""" """
resource = PygmentsFormResource form = PygmentsForm
def get(self, request): def get(self, request):
""" """