mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-29 13:04:03 +03:00
Various cleanup
This commit is contained in:
parent
5557dfb54c
commit
764fbe335f
|
@ -1,5 +1,4 @@
|
||||||
from django.template import RequestContext, loader
|
from django.template import RequestContext, loader
|
||||||
from django.core.handlers.wsgi import STATUS_CODE_TEXT
|
|
||||||
import json
|
import json
|
||||||
from utils import dict2xml
|
from utils import dict2xml
|
||||||
|
|
||||||
|
@ -22,14 +21,6 @@ class TemplatedEmitter(BaseEmitter):
|
||||||
template = loader.get_template(self.template)
|
template = loader.get_template(self.template)
|
||||||
context = RequestContext(self.resource.request, {
|
context = RequestContext(self.resource.request, {
|
||||||
'content': content,
|
'content': content,
|
||||||
'status': self.resource.resp_status,
|
|
||||||
'reason': STATUS_CODE_TEXT.get(self.resource.resp_status, ''),
|
|
||||||
'headers': self.resource.resp_headers,
|
|
||||||
'resource_name': self.resource.__class__.__name__,
|
|
||||||
'resource_doc': self.resource.__doc__,
|
|
||||||
'create_form': self.resource.form,
|
|
||||||
'update_form': self.resource.form,
|
|
||||||
'request': self.resource.request,
|
|
||||||
'resource': self.resource,
|
'resource': self.resource,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.core.handlers.wsgi import STATUS_CODE_TEXT
|
||||||
from rest import emitters, parsers
|
from rest import emitters, parsers
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
import re
|
||||||
|
|
||||||
#
|
#
|
||||||
STATUS_400_BAD_REQUEST = 400
|
STATUS_400_BAD_REQUEST = 400
|
||||||
|
@ -22,6 +24,10 @@ class ResourceException(Exception):
|
||||||
class Resource(object):
|
class Resource(object):
|
||||||
# List of RESTful operations which may be performed on this resource.
|
# List of RESTful operations which may be performed on this resource.
|
||||||
allowed_operations = ('read',)
|
allowed_operations = ('read',)
|
||||||
|
anon_allowed_operations = ()
|
||||||
|
|
||||||
|
# Optional form for input validation and presentation of HTML formatted responses.
|
||||||
|
form = None
|
||||||
|
|
||||||
# List of content-types the resource can respond with, ordered by preference
|
# List of content-types the resource can respond with, ordered by preference
|
||||||
emitters = ( ('application/json', emitters.JSONEmitter),
|
emitters = ( ('application/json', emitters.JSONEmitter),
|
||||||
|
@ -36,9 +42,6 @@ class Resource(object):
|
||||||
'application/x-www-form-urlencoded': parsers.FormParser,
|
'application/x-www-form-urlencoded': parsers.FormParser,
|
||||||
'multipart/form-data': parsers.FormParser }
|
'multipart/form-data': parsers.FormParser }
|
||||||
|
|
||||||
# Optional form for input validation and presentation of HTML formatted responses.
|
|
||||||
form = None
|
|
||||||
|
|
||||||
# Map standard HTTP methods to RESTful operations
|
# Map standard HTTP methods to RESTful operations
|
||||||
CALLMAP = { 'GET': 'read', 'POST': 'create',
|
CALLMAP = { 'GET': 'read', 'POST': 'create',
|
||||||
'PUT': 'update', 'DELETE': 'delete' }
|
'PUT': 'update', 'DELETE': 'delete' }
|
||||||
|
@ -57,20 +60,34 @@ class Resource(object):
|
||||||
"""Make the class callable so it can be used as a Django view."""
|
"""Make the class callable so it can be used as a Django view."""
|
||||||
self = object.__new__(cls)
|
self = object.__new__(cls)
|
||||||
self.__init__()
|
self.__init__()
|
||||||
self.request = request
|
|
||||||
try:
|
|
||||||
return self._handle_request(request, *args, **kwargs)
|
return self._handle_request(request, *args, **kwargs)
|
||||||
except:
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def name(self):
|
||||||
|
"""Provide a name for the resource.
|
||||||
|
By default this is the class name, with 'CamelCaseNames' converted to 'Camel Case Names',
|
||||||
|
although this behaviour may be overridden."""
|
||||||
|
class_name = self.__class__.__name__
|
||||||
|
return re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', ' \\1', class_name).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def description(self):
|
||||||
|
"""Provide a description for the resource.
|
||||||
|
By default this is the class's docstring,
|
||||||
|
although this behaviour may be overridden."""
|
||||||
|
return "%s" % self.__doc__
|
||||||
|
|
||||||
|
|
||||||
|
def resp_status_text(self):
|
||||||
|
"""Return reason text corrosponding to our HTTP response status code.
|
||||||
|
Provided for convienience."""
|
||||||
|
return STATUS_CODE_TEXT.get(self.resp_status, '')
|
||||||
|
|
||||||
|
|
||||||
def reverse(self, view, *args, **kwargs):
|
def reverse(self, view, *args, **kwargs):
|
||||||
"""Return a fully qualified URI for a given view or resource, using the current request as the base URI.
|
"""Return a fully qualified URI for a given view or resource, using the current request as the base URI.
|
||||||
TODO: Add SITEMAP option.
|
TODO: Add SITEMAP option.
|
||||||
|
@ -125,8 +142,14 @@ class Resource(object):
|
||||||
return method
|
return method
|
||||||
|
|
||||||
|
|
||||||
|
def authenticate(self):
|
||||||
|
"""..."""
|
||||||
|
# user = ...
|
||||||
|
# if anon_user and not anon_allowed_operations raise PermissionDenied
|
||||||
|
# return
|
||||||
|
|
||||||
def check_method_allowed(self, method):
|
def check_method_allowed(self, method):
|
||||||
"""Ensure the request method is acceptable fot this resource."""
|
"""Ensure the request method is acceptable for this resource."""
|
||||||
if not method in self.CALLMAP.keys():
|
if not method in self.CALLMAP.keys():
|
||||||
raise ResourceException(STATUS_501_NOT_IMPLEMENTED,
|
raise ResourceException(STATUS_501_NOT_IMPLEMENTED,
|
||||||
{'detail': 'Unknown or unsupported method \'%s\'' % method})
|
{'detail': 'Unknown or unsupported method \'%s\'' % method})
|
||||||
|
@ -137,13 +160,12 @@ class Resource(object):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def determine_form(self, data=None, is_response=False):
|
def get_bound_form(self, data=None, is_response=False):
|
||||||
"""Optionally return a Django Form instance, which may be used for validation
|
"""Optionally return a Django Form instance, which may be used for validation
|
||||||
and/or rendered by an HTML/XHTML emitter.
|
and/or rendered by an HTML/XHTML emitter.
|
||||||
|
|
||||||
If data is not None the form will be bound to data. is_response indicates if data should be
|
If data is not None the form will be bound to data. is_response indicates if data should be
|
||||||
treated as the input data (bind to client input) or the response data (bind to an existing object).
|
treated as the input data (bind to client input) or the response data (bind to an existing object)."""
|
||||||
"""
|
|
||||||
if self.form:
|
if self.form:
|
||||||
if data:
|
if data:
|
||||||
return self.form(data)
|
return self.form(data)
|
||||||
|
@ -156,15 +178,25 @@ class Resource(object):
|
||||||
"""Perform any resource-specific data deserialization and/or validation
|
"""Perform any resource-specific data deserialization and/or validation
|
||||||
after the initial HTTP content-type deserialization has taken place.
|
after the initial HTTP content-type deserialization has taken place.
|
||||||
|
|
||||||
|
Returns a tuple containing the cleaned up data, and optionally a form bound to that data.
|
||||||
|
|
||||||
By default this uses form validation to filter the basic input into the required types."""
|
By default this uses form validation to filter the basic input into the required types."""
|
||||||
if self.form is None:
|
if self.form is None:
|
||||||
return data
|
return (data, None)
|
||||||
|
|
||||||
|
form_instance = self.get_bound_form(data)
|
||||||
|
|
||||||
|
if not form_instance.is_valid():
|
||||||
|
if not form_instance.errors:
|
||||||
|
details = 'No content was supplied'
|
||||||
|
else:
|
||||||
|
details = dict((key, map(unicode, val)) for (key, val) in form_instance.errors.iteritems())
|
||||||
|
if form_instance.non_field_errors():
|
||||||
|
details['_extra'] = self.form.non_field_errors()
|
||||||
|
|
||||||
if not self.form.is_valid():
|
|
||||||
details = dict((key, map(unicode, val)) for (key, val) in self.form.errors.iteritems())
|
|
||||||
raise ResourceException(STATUS_400_BAD_REQUEST, {'detail': details})
|
raise ResourceException(STATUS_400_BAD_REQUEST, {'detail': details})
|
||||||
|
|
||||||
return self.form.cleaned_data
|
return (form_instance.cleaned_data, form_instance)
|
||||||
|
|
||||||
|
|
||||||
def cleanup_response(self, data):
|
def cleanup_response(self, data):
|
||||||
|
@ -188,7 +220,7 @@ class Resource(object):
|
||||||
return self.parsers[content_type]
|
return self.parsers[content_type]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ResourceException(STATUS_415_UNSUPPORTED_MEDIA_TYPE,
|
raise ResourceException(STATUS_415_UNSUPPORTED_MEDIA_TYPE,
|
||||||
{'detail': 'Unsupported content type \'%s\'' % content_type})
|
{'detail': 'Unsupported media type \'%s\'' % content_type})
|
||||||
|
|
||||||
|
|
||||||
def determine_emitter(self, request):
|
def determine_emitter(self, request):
|
||||||
|
@ -253,13 +285,13 @@ class Resource(object):
|
||||||
5. serialize response data into response content, using standard HTTP content negotiation
|
5. serialize response data into response content, using standard HTTP content negotiation
|
||||||
"""
|
"""
|
||||||
emitter = None
|
emitter = None
|
||||||
|
method = self.determine_method(request)
|
||||||
|
|
||||||
# We make these attributes to allow for a certain amount of munging,
|
# We make these attributes to allow for a certain amount of munging,
|
||||||
# eg The HTML emitter needs to render this information
|
# eg The HTML emitter needs to render this information
|
||||||
self.method = self.determine_method(request)
|
self.request = request
|
||||||
self.form = None
|
self.form_instance = None
|
||||||
self.resp_status = None
|
self.resp_status = None
|
||||||
self.resp_content = None
|
|
||||||
self.resp_headers = {}
|
self.resp_headers = {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -267,30 +299,29 @@ class Resource(object):
|
||||||
mimetype, emitter = self.determine_emitter(request)
|
mimetype, emitter = self.determine_emitter(request)
|
||||||
|
|
||||||
# Ensure the requested operation is permitted on this resource
|
# Ensure the requested operation is permitted on this resource
|
||||||
self.check_method_allowed(self.method)
|
self.check_method_allowed(method)
|
||||||
|
|
||||||
# Get the appropriate create/read/update/delete function
|
# Get the appropriate create/read/update/delete function
|
||||||
func = getattr(self, self.CALLMAP.get(self.method, ''))
|
func = getattr(self, self.CALLMAP.get(method, ''))
|
||||||
|
|
||||||
# Either generate the response data, deserializing and validating any request data
|
# Either generate the response data, deserializing and validating any request data
|
||||||
if self.method in ('PUT', 'POST'):
|
if method in ('PUT', 'POST'):
|
||||||
parser = self.determine_parser(request)
|
parser = self.determine_parser(request)
|
||||||
data = parser(self).parse(request.raw_post_data)
|
data = parser(self).parse(request.raw_post_data)
|
||||||
self.form = self.determine_form(data)
|
(data, self.form_instance) = self.cleanup_request(data)
|
||||||
data = self.cleanup_request(data)
|
|
||||||
(self.resp_status, ret, self.resp_headers) = func(data, request.META, *args, **kwargs)
|
(self.resp_status, ret, self.resp_headers) = func(data, request.META, *args, **kwargs)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
(self.resp_status, ret, self.resp_headers) = func(request.META, *args, **kwargs)
|
(self.resp_status, ret, self.resp_headers) = func(request.META, *args, **kwargs)
|
||||||
self.form = self.determine_form(ret, is_response=True)
|
self.form_instance = self.get_bound_form(ret, is_response=True)
|
||||||
|
|
||||||
|
|
||||||
except ResourceException, exc:
|
except ResourceException, exc:
|
||||||
(self.resp_status, ret, self.resp_headers) = (exc.status, exc.content, exc.headers)
|
(self.resp_status, ret, self.resp_headers) = (exc.status, exc.content, exc.headers)
|
||||||
if emitter is None:
|
if emitter is None:
|
||||||
mimetype, emitter = self.emitters[0]
|
mimetype, emitter = self.emitters[0]
|
||||||
if self.form is None:
|
if self.form_instance is None:
|
||||||
self.form = self.determine_form()
|
self.form_instance = self.get_bound_form()
|
||||||
|
|
||||||
|
|
||||||
# Always add the allow header
|
# Always add the allow header
|
||||||
|
@ -315,17 +346,16 @@ from django.db.models.query import QuerySet
|
||||||
from django.db.models import Model
|
from django.db.models import Model
|
||||||
import decimal
|
import decimal
|
||||||
import inspect
|
import inspect
|
||||||
import re
|
|
||||||
|
|
||||||
class ModelResource(Resource):
|
class ModelResource(Resource):
|
||||||
model = None
|
model = None
|
||||||
fields = None
|
fields = None
|
||||||
form_fields = None
|
form_fields = None
|
||||||
|
|
||||||
def determine_form(self, data=None, is_response=False):
|
def get_bound_form(self, data=None, is_response=False):
|
||||||
"""Return a form that may be used in validation and/or rendering an html emitter"""
|
"""Return a form that may be used in validation and/or rendering an html emitter"""
|
||||||
if self.form:
|
if self.form:
|
||||||
return super(self.__class__, self).determine_form(data, is_response=is_response)
|
return super(self.__class__, self).get_bound_form(data, is_response=is_response)
|
||||||
|
|
||||||
elif self.model:
|
elif self.model:
|
||||||
class NewModelForm(ModelForm):
|
class NewModelForm(ModelForm):
|
||||||
|
@ -640,7 +670,7 @@ class ModelResource(Resource):
|
||||||
class QueryModelResource(ModelResource):
|
class QueryModelResource(ModelResource):
|
||||||
allowed_methods = ('read',)
|
allowed_methods = ('read',)
|
||||||
|
|
||||||
def determine_form(self, data=None, is_response=False):
|
def get_bound_form(self, data=None, is_response=False):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def read(self, headers={}, *args, **kwargs):
|
def read(self, headers={}, *args, **kwargs):
|
||||||
|
|
|
@ -7,26 +7,27 @@
|
||||||
pre {border: 1px solid black; padding: 1em; background: #ffd}
|
pre {border: 1px solid black; padding: 1em; background: #ffd}
|
||||||
div.action {padding: 0.5em 1em; margin-bottom: 0.5em; background: #ddf}
|
div.action {padding: 0.5em 1em; margin-bottom: 0.5em; background: #ddf}
|
||||||
</style>
|
</style>
|
||||||
|
<title>API - {{ resource.name }}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>{{ resource_name }}</h1>
|
<h1>{{ resource.name }}</h1>
|
||||||
<p>{{ resource_doc }}</p>
|
<p>{{ resource.description }}</p>
|
||||||
<pre><b>{{ status }} {{ reason }}</b>{% autoescape off %}
|
<pre><b>{{ resource.resp_status }} {{ resource.resp_status_text }}</b>{% autoescape off %}
|
||||||
{% for key, val in headers.items %}<b>{{ key }}:</b> {{ val|urlize_quoted_links }}
|
{% for key, val in resource.resp_headers.items %}<b>{{ key }}:</b> {{ val|urlize_quoted_links }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{{ content|urlize_quoted_links }} </pre>{% endautoescape %}
|
{{ content|urlize_quoted_links }} </pre>{% endautoescape %}
|
||||||
|
|
||||||
{% if 'read' in resource.allowed_operations %}
|
{% if 'read' in resource.allowed_operations %}
|
||||||
<div class='action'>
|
<div class='action'>
|
||||||
<a href='{{ request.path }}'>Read</a>
|
<a href='{{ resource.request.path }}'>Read</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if 'create' in resource.allowed_operations %}
|
{% if 'create' in resource.allowed_operations %}
|
||||||
<div class='action'>
|
<div class='action'>
|
||||||
<form action="{{ request.path }}" method="POST">
|
<form action="{{ resource.request.path }}" method="POST">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ create_form.as_p }}
|
{{ resource.form_instance.as_p }}
|
||||||
<input type="submit" value="Create" />
|
<input type="submit" value="Create" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,10 +35,10 @@
|
||||||
|
|
||||||
{% if 'update' in resource.allowed_operations %}
|
{% if 'update' in resource.allowed_operations %}
|
||||||
<div class='action'>
|
<div class='action'>
|
||||||
<form action="{{ request.path }}" method="POST">
|
<form action="{{ resource.request.path }}" method="POST">
|
||||||
<input type="hidden" name="{{ resource.METHOD_PARAM}}" value="PUT" />
|
<input type="hidden" name="{{ resource.METHOD_PARAM}}" value="PUT" />
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ create_form.as_p }}
|
{{ resource.form_instance.as_p }}
|
||||||
<input type="submit" value="Update" />
|
<input type="submit" value="Update" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -45,7 +46,7 @@
|
||||||
|
|
||||||
{% if 'delete' in resource.allowed_operations %}
|
{% if 'delete' in resource.allowed_operations %}
|
||||||
<div class='action'>
|
<div class='action'>
|
||||||
<form action="{{ request.path }}" method="POST">
|
<form action="{{ resource.request.path }}" method="POST">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="{{ resource.METHOD_PARAM}}" value="DELETE" />
|
<input type="hidden" name="{{ resource.METHOD_PARAM}}" value="DELETE" />
|
||||||
<input type="submit" value="Delete" />
|
<input type="submit" value="Delete" />
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
{% autoescape off %}HTTP Status {{ status }}
|
{{ resource.name }}
|
||||||
{% for key, val in headers.items %}{{ key }}: {{ val }}
|
{{ resource.description }}
|
||||||
|
|
||||||
|
{% autoescape off %}HTTP/1.0 {{ resource.resp_status }} {{ resource.resp_status_text }}
|
||||||
|
{% for key, val in resource.resp_headers.items %}{{ key }}: {{ val }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{{ content }}{% endautoescape %}
|
{{ content }}{% endautoescape %}
|
|
@ -1,6 +1,8 @@
|
||||||
from rest.resource import Resource, ModelResource, QueryModelResource
|
from rest.resource import Resource, ModelResource, QueryModelResource
|
||||||
from testapp.models import BlogPost, Comment
|
from testapp.models import BlogPost, Comment
|
||||||
|
|
||||||
|
##### Root Resource #####
|
||||||
|
|
||||||
class RootResource(Resource):
|
class RootResource(Resource):
|
||||||
"""This is the top level resource for the API.
|
"""This is the top level resource for the API.
|
||||||
All the sub-resources are discoverable from here."""
|
All the sub-resources are discoverable from here."""
|
||||||
|
@ -11,48 +13,52 @@ class RootResource(Resource):
|
||||||
'blog-post': self.reverse(BlogPostCreator)}, {})
|
'blog-post': self.reverse(BlogPostCreator)}, {})
|
||||||
|
|
||||||
|
|
||||||
# Blog Post Resources
|
##### Blog Post Resources #####
|
||||||
|
|
||||||
|
BLOG_POST_FIELDS = ('created', 'title', 'slug', 'content', 'absolute_url', 'comment_url', 'comments_url')
|
||||||
|
|
||||||
class BlogPostList(QueryModelResource):
|
class BlogPostList(QueryModelResource):
|
||||||
"""A resource which lists all existing blog posts."""
|
"""A resource which lists all existing blog posts."""
|
||||||
allowed_operations = ('read', )
|
allowed_operations = ('read', )
|
||||||
model = BlogPost
|
model = BlogPost
|
||||||
|
fields = BLOG_POST_FIELDS
|
||||||
|
|
||||||
class BlogPostCreator(ModelResource):
|
class BlogPostCreator(ModelResource):
|
||||||
"""A resource with which blog posts may be created."""
|
"""A resource with which blog posts may be created."""
|
||||||
allowed_operations = ('create',)
|
allowed_operations = ('create',)
|
||||||
model = BlogPost
|
model = BlogPost
|
||||||
fields = ('created', 'title', 'slug', 'content', 'absolute_url', 'comment_url', 'comments_url')
|
fields = BLOG_POST_FIELDS
|
||||||
|
|
||||||
|
|
||||||
class BlogPostInstance(ModelResource):
|
class BlogPostInstance(ModelResource):
|
||||||
"""A resource which represents a single blog post."""
|
"""A resource which represents a single blog post."""
|
||||||
allowed_operations = ('read', 'update', 'delete')
|
allowed_operations = ('read', 'update', 'delete')
|
||||||
model = BlogPost
|
model = BlogPost
|
||||||
fields = ('created', 'title', 'slug', 'content', 'absolute_url', 'comment_url', 'comments_url')
|
fields = BLOG_POST_FIELDS
|
||||||
|
|
||||||
|
|
||||||
# Comment Resources
|
##### Comment Resources #####
|
||||||
|
|
||||||
|
COMMENT_FIELDS = ('username', 'comment', 'created', 'rating', 'absolute_url', 'blogpost_url')
|
||||||
|
|
||||||
class CommentList(QueryModelResource):
|
class CommentList(QueryModelResource):
|
||||||
"""A resource which lists all existing comments for a given blog post."""
|
"""A resource which lists all existing comments for a given blog post."""
|
||||||
allowed_operations = ('read', )
|
allowed_operations = ('read', )
|
||||||
model = Comment
|
model = Comment
|
||||||
|
fields = COMMENT_FIELDS
|
||||||
|
|
||||||
class CommentCreator(ModelResource):
|
class CommentCreator(ModelResource):
|
||||||
"""A resource with which blog comments may be created for a given blog post."""
|
"""A resource with which blog comments may be created for a given blog post."""
|
||||||
allowed_operations = ('create',)
|
allowed_operations = ('create',)
|
||||||
model = Comment
|
model = Comment
|
||||||
fields = ('username', 'comment', 'created', 'rating', 'absolute_url', 'blogpost_url')
|
fields = COMMENT_FIELDS
|
||||||
|
|
||||||
|
|
||||||
class CommentInstance(ModelResource):
|
class CommentInstance(ModelResource):
|
||||||
"""A resource which represents a single comment."""
|
"""A resource which represents a single comment."""
|
||||||
allowed_operations = ('read', 'update', 'delete')
|
allowed_operations = ('read', 'update', 'delete')
|
||||||
model = Comment
|
model = Comment
|
||||||
fields = ('username', 'comment', 'created', 'rating', 'absolute_url', 'blogpost_url')
|
fields = COMMENT_FIELDS
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
#'read-only-api': self.reverse(ReadOnlyResource),
|
#'read-only-api': self.reverse(ReadOnlyResource),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user