mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-06-27 08:53:19 +03:00
emitters -> renderers
This commit is contained in:
parent
b358fbdbe9
commit
8756664e06
|
@ -6,6 +6,7 @@ from djangorestframework import status
|
||||||
|
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.http.multipartparser import LimitBytes # TODO: Use LimitedStream in compat
|
from django.http.multipartparser import LimitBytes # TODO: Use LimitedStream in compat
|
||||||
|
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import re
|
import re
|
||||||
|
@ -233,7 +234,7 @@ class RequestMixin(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def default_parser(self):
|
def default_parser(self):
|
||||||
"""Return the view's most preffered renderer.
|
"""Return the view's most preferred renderer.
|
||||||
(This has no behavioural effect, but is may be used by documenting renderers)"""
|
(This has no behavioural effect, but is may be used by documenting renderers)"""
|
||||||
return self.parsers[0]
|
return self.parsers[0]
|
||||||
|
|
||||||
|
@ -437,3 +438,90 @@ class AuthMixin(object):
|
||||||
'You may need to login or otherwise authenticate the request.'})
|
'You may need to login or otherwise authenticate the request.'})
|
||||||
|
|
||||||
|
|
||||||
|
########## Model Mixins ##########
|
||||||
|
|
||||||
|
class ReadModelMixin(object):
|
||||||
|
"""Behaviour to read a model instance on GET requests"""
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
if args:
|
||||||
|
# If we have any none kwargs then assume the last represents the primrary key
|
||||||
|
instance = self.model.objects.get(pk=args[-1], **kwargs)
|
||||||
|
else:
|
||||||
|
# Otherwise assume the kwargs uniquely identify the model
|
||||||
|
instance = self.model.objects.get(**kwargs)
|
||||||
|
except self.model.DoesNotExist:
|
||||||
|
raise ErrorResponse(status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class CreateModelMixin(object):
|
||||||
|
"""Behaviour to create a model instance on POST requests"""
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
# translated 'related_field' kwargs into 'related_field_id'
|
||||||
|
for related_name in [field.name for field in self.model._meta.fields if isinstance(field, RelatedField)]:
|
||||||
|
if kwargs.has_key(related_name):
|
||||||
|
kwargs[related_name + '_id'] = kwargs[related_name]
|
||||||
|
del kwargs[related_name]
|
||||||
|
|
||||||
|
all_kw_args = dict(self.CONTENT.items() + kwargs.items())
|
||||||
|
if args:
|
||||||
|
instance = self.model(pk=args[-1], **all_kw_args)
|
||||||
|
else:
|
||||||
|
instance = self.model(**all_kw_args)
|
||||||
|
instance.save()
|
||||||
|
headers = {}
|
||||||
|
if hasattr(instance, 'get_absolute_url'):
|
||||||
|
headers['Location'] = instance.get_absolute_url()
|
||||||
|
return Response(status.HTTP_201_CREATED, instance, headers)
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateModelMixin(object):
|
||||||
|
"""Behaviour to update a model instance on PUT requests"""
|
||||||
|
def put(self, request, *args, **kwargs):
|
||||||
|
# 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:
|
||||||
|
if args:
|
||||||
|
# If we have any none kwargs then assume the last represents the primrary key
|
||||||
|
instance = self.model.objects.get(pk=args[-1], **kwargs)
|
||||||
|
else:
|
||||||
|
# Otherwise assume the kwargs uniquely identify the model
|
||||||
|
instance = self.model.objects.get(**kwargs)
|
||||||
|
|
||||||
|
for (key, val) in self.CONTENT.items():
|
||||||
|
setattr(instance, key, val)
|
||||||
|
except self.model.DoesNotExist:
|
||||||
|
instance = self.model(**self.CONTENT)
|
||||||
|
instance.save()
|
||||||
|
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteModelMixin(object):
|
||||||
|
"""Behaviour to delete a model instance on DELETE requests"""
|
||||||
|
def delete(self, request, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
if args:
|
||||||
|
# If we have any none kwargs then assume the last represents the primrary key
|
||||||
|
instance = self.model.objects.get(pk=args[-1], **kwargs)
|
||||||
|
else:
|
||||||
|
# Otherwise assume the kwargs uniquely identify the model
|
||||||
|
instance = self.model.objects.get(**kwargs)
|
||||||
|
except self.model.DoesNotExist:
|
||||||
|
raise ErrorResponse(status.HTTP_404_NOT_FOUND, None, {})
|
||||||
|
|
||||||
|
instance.delete()
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class ListModelMixin(object):
|
||||||
|
"""Behaviour to list a set of model instances on GET requests"""
|
||||||
|
queryset = None
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
queryset = self.queryset if self.queryset else self.model.objects.all()
|
||||||
|
return queryset.filter(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -341,94 +341,16 @@ class ModelResource(Resource):
|
||||||
return _any(data, self.fields)
|
return _any(data, self.fields)
|
||||||
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
try:
|
|
||||||
if args:
|
|
||||||
# If we have any none kwargs then assume the last represents the primrary key
|
|
||||||
instance = self.model.objects.get(pk=args[-1], **kwargs)
|
|
||||||
else:
|
|
||||||
# Otherwise assume the kwargs uniquely identify the model
|
|
||||||
instance = self.model.objects.get(**kwargs)
|
|
||||||
except self.model.DoesNotExist:
|
|
||||||
raise ErrorResponse(status.HTTP_404_NOT_FOUND)
|
|
||||||
|
|
||||||
return instance
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
# TODO: test creation on a non-existing resource url
|
|
||||||
|
|
||||||
# translated related_field into related_field_id
|
|
||||||
for related_name in [field.name for field in self.model._meta.fields if isinstance(field, RelatedField)]:
|
|
||||||
if kwargs.has_key(related_name):
|
|
||||||
kwargs[related_name + '_id'] = kwargs[related_name]
|
|
||||||
del kwargs[related_name]
|
|
||||||
|
|
||||||
all_kw_args = dict(self.CONTENT.items() + kwargs.items())
|
|
||||||
if args:
|
|
||||||
instance = self.model(pk=args[-1], **all_kw_args)
|
|
||||||
else:
|
|
||||||
instance = self.model(**all_kw_args)
|
|
||||||
instance.save()
|
|
||||||
headers = {}
|
|
||||||
if hasattr(instance, 'get_absolute_url'):
|
|
||||||
headers['Location'] = instance.get_absolute_url()
|
|
||||||
return Response(status.HTTP_201_CREATED, instance, headers)
|
|
||||||
|
|
||||||
def put(self, request, *args, **kwargs):
|
|
||||||
# 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:
|
|
||||||
if args:
|
|
||||||
# If we have any none kwargs then assume the last represents the primrary key
|
|
||||||
instance = self.model.objects.get(pk=args[-1], **kwargs)
|
|
||||||
else:
|
|
||||||
# Otherwise assume the kwargs uniquely identify the model
|
|
||||||
instance = self.model.objects.get(**kwargs)
|
|
||||||
|
|
||||||
for (key, val) in self.CONTENT.items():
|
|
||||||
setattr(instance, key, val)
|
|
||||||
except self.model.DoesNotExist:
|
|
||||||
instance = self.model(**self.CONTENT)
|
|
||||||
instance.save()
|
|
||||||
|
|
||||||
instance.save()
|
|
||||||
return instance
|
|
||||||
|
|
||||||
def delete(self, request, *args, **kwargs):
|
|
||||||
try:
|
|
||||||
if args:
|
|
||||||
# If we have any none kwargs then assume the last represents the primrary key
|
|
||||||
instance = self.model.objects.get(pk=args[-1], **kwargs)
|
|
||||||
else:
|
|
||||||
# Otherwise assume the kwargs uniquely identify the model
|
|
||||||
instance = self.model.objects.get(**kwargs)
|
|
||||||
except self.model.DoesNotExist:
|
|
||||||
raise ErrorResponse(status.HTTP_404_NOT_FOUND, None, {})
|
|
||||||
|
|
||||||
instance.delete()
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class InstanceModelResource(ModelResource):
|
class InstanceModelResource(ReadModelMixin, UpdateModelMixin, DeleteModelMixin, ModelResource):
|
||||||
http_method_names = ['get', 'put', 'delete', 'head', 'options', 'trace', 'patch'] # Bit of a hack, these - needs fixing.
|
"""A view which provides default operations for read/update/delete against a model instance."""
|
||||||
|
pass
|
||||||
|
|
||||||
class RootModelResource(ModelResource):
|
class ListOrCreateModelResource(CreateModelMixin, ListModelMixin, ModelResource):
|
||||||
"""A Resource which provides default operations for list and create."""
|
"""A Resource which provides default operations for list and create."""
|
||||||
queryset = None
|
pass
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
class ListModelResource(ListModelMixin, ModelResource):
|
||||||
queryset = self.queryset if self.queryset else self.model.objects.all()
|
"""Resource with default operations for list."""
|
||||||
return queryset.filter(**kwargs)
|
pass
|
||||||
|
|
||||||
http_method_names = ['get', 'post', 'head', 'options', 'trace', 'patch']
|
|
||||||
|
|
||||||
class QueryModelResource(ModelResource):
|
|
||||||
"""Resource with default operations for list.
|
|
||||||
TODO: provide filter/order/num_results/paging, and a create operation to create queries."""
|
|
||||||
allowed_methods = ('GET',)
|
|
||||||
queryset = None
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
queryset = self.queryset if self.queryset else self.model.objects.all()
|
|
||||||
return queryset.filer(**kwargs)
|
|
||||||
|
|
||||||
http_method_names = ['get', 'head', 'options', 'trace', 'patch']
|
|
|
@ -43,7 +43,7 @@ class BaseRenderer(object):
|
||||||
|
|
||||||
class TemplateRenderer(BaseRenderer):
|
class TemplateRenderer(BaseRenderer):
|
||||||
"""Provided for convienience.
|
"""Provided for convienience.
|
||||||
Emit the output by simply rendering it with the given template."""
|
Render the output by simply rendering it with the given template."""
|
||||||
media_type = None
|
media_type = None
|
||||||
template = None
|
template = None
|
||||||
|
|
||||||
|
|
|
@ -19,14 +19,12 @@ class Resource(RequestMixin, ResponseMixin, AuthMixin, View):
|
||||||
"""Handles incoming requests and maps them to REST operations.
|
"""Handles incoming requests and maps them to REST operations.
|
||||||
Performs request deserialization, response serialization, authentication and input validation."""
|
Performs request deserialization, response serialization, authentication and input validation."""
|
||||||
|
|
||||||
http_method_names = ['get', 'post', 'put', 'delete', 'head', 'options', 'trace', 'patch']
|
|
||||||
|
|
||||||
# List of renderers the resource can serialize the response with, ordered by preference.
|
# List of renderers the resource can serialize the response with, ordered by preference.
|
||||||
renderers = ( renderers.JSONRenderer,
|
renderers = ( renderers.JSONRenderer,
|
||||||
renderers.DocumentingHTMLRenderer,
|
renderers.DocumentingHTMLRenderer,
|
||||||
renderers.DocumentingXHTMLRenderer,
|
renderers.DocumentingXHTMLRenderer,
|
||||||
renderers.DocumentingPlainTextRenderer,
|
renderers.DocumentingPlainTextRenderer,
|
||||||
renderers.XMLRenderer )
|
renderers.XMLRenderer )
|
||||||
|
|
||||||
# List of parsers the resource can parse the request with.
|
# List of parsers the resource can parse the request with.
|
||||||
parsers = ( parsers.JSONParser,
|
parsers = ( parsers.JSONParser,
|
||||||
|
|
|
@ -12,6 +12,8 @@ RATING_CHOICES = ((0, 'Awful'),
|
||||||
(3, 'Good'),
|
(3, 'Good'),
|
||||||
(4, 'Excellent'))
|
(4, 'Excellent'))
|
||||||
|
|
||||||
|
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)
|
||||||
|
@ -38,9 +40,10 @@ class BlogPost(models.Model):
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
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('-pk')[10:]:
|
for obj in self.__class__.objects.order_by('-pk')[MAX_POSTS:]:
|
||||||
obj.delete()
|
obj.delete()
|
||||||
|
|
||||||
|
|
||||||
class Comment(models.Model):
|
class Comment(models.Model):
|
||||||
blogpost = models.ForeignKey(BlogPost, editable=False, related_name='comments')
|
blogpost = models.ForeignKey(BlogPost, editable=False, related_name='comments')
|
||||||
username = models.CharField(max_length=128)
|
username = models.CharField(max_length=128)
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
from djangorestframework.modelresource import InstanceModelResource, RootModelResource
|
from djangorestframework.modelresource import InstanceModelResource, ListOrCreateModelResource
|
||||||
|
|
||||||
from blogpost import models
|
from blogpost import models
|
||||||
|
|
||||||
BLOG_POST_FIELDS = ('created', 'title', 'slug', 'content', 'absolute_url', 'comment_url', 'comments_url')
|
BLOG_POST_FIELDS = ('created', 'title', 'slug', 'content', 'absolute_url', 'comment_url', 'comments_url')
|
||||||
COMMENT_FIELDS = ('username', 'comment', 'created', 'rating', 'absolute_url', 'blogpost_url')
|
COMMENT_FIELDS = ('username', 'comment', 'created', 'rating', 'absolute_url', 'blogpost_url')
|
||||||
MAX_POSTS = 10
|
|
||||||
|
|
||||||
class BlogPosts(RootModelResource):
|
class BlogPosts(ListOrCreateModelResource):
|
||||||
"""A resource with which lists all existing blog posts and creates new blog posts."""
|
"""A resource with which lists all existing blog posts and creates new blog posts."""
|
||||||
model = models.BlogPost
|
model = models.BlogPost
|
||||||
fields = BLOG_POST_FIELDS
|
fields = BLOG_POST_FIELDS
|
||||||
|
@ -16,7 +15,7 @@ class BlogPostInstance(InstanceModelResource):
|
||||||
model = models.BlogPost
|
model = models.BlogPost
|
||||||
fields = BLOG_POST_FIELDS
|
fields = BLOG_POST_FIELDS
|
||||||
|
|
||||||
class Comments(RootModelResource):
|
class Comments(ListOrCreateModelResource):
|
||||||
"""A resource which lists all existing comments for a given blog post, and creates new blog comments for a given blog post."""
|
"""A resource which lists all existing comments for a given blog post, and creates new blog comments for a given blog post."""
|
||||||
model = models.Comment
|
model = models.Comment
|
||||||
fields = COMMENT_FIELDS
|
fields = COMMENT_FIELDS
|
||||||
|
|
|
@ -15,7 +15,7 @@ class ExampleView(ResponseMixin, View):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
response = Response(200, {'description': 'Some example content',
|
response = Response(200, {'description': 'Some example content',
|
||||||
'url': reverse('mixin-view')})
|
'url': reverse('mixin-view')})
|
||||||
return self.emit(response)
|
return self.render(response)
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
|
|
|
@ -68,7 +68,7 @@ class PygmentsRoot(Resource):
|
||||||
|
|
||||||
class PygmentsInstance(Resource):
|
class PygmentsInstance(Resource):
|
||||||
"""Simply return the stored highlighted HTML file with the correct mime type.
|
"""Simply return the stored highlighted HTML file with the correct mime type.
|
||||||
This Resource only emits HTML and uses a standard HTML renderer rather than the renderers.DocumentingHTMLRenderer class."""
|
This Resource only renders HTML and uses a standard HTML renderer rather than the renderers.DocumentingHTMLRenderer class."""
|
||||||
renderers = (HTMLRenderer,)
|
renderers = (HTMLRenderer,)
|
||||||
|
|
||||||
def get(self, request, unique_id):
|
def get(self, request, unique_id):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user