mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-24 02:24:03 +03:00
Merge branch 'master' of https://github.com/tomchristie/django-rest-framework
This commit is contained in:
commit
7557777c66
6
AUTHORS
6
AUTHORS
|
@ -34,6 +34,12 @@ Paul Oswald <poswald>
|
||||||
Sean C. Farley <scfarley>
|
Sean C. Farley <scfarley>
|
||||||
Daniel Izquierdo <izquierdo>
|
Daniel Izquierdo <izquierdo>
|
||||||
Can Yavuz <tschan>
|
Can Yavuz <tschan>
|
||||||
|
Shawn Lewis <shawnlewis>
|
||||||
|
Adam Ness <greylurk>
|
||||||
|
<yetist>
|
||||||
|
Max Arnold <max-arnold>
|
||||||
|
Ralph Broenink <ralphje>
|
||||||
|
Simon Pantzare <pilt>
|
||||||
|
|
||||||
THANKS TO:
|
THANKS TO:
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
Release Notes
|
Release Notes
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
0.4.0-dev
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Markdown < 2.0 is no longer supported.
|
||||||
|
|
||||||
0.3.3
|
0.3.3
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ We also have a `Jenkins service <http://jenkins.tibold.nl/job/djangorestframewor
|
||||||
Requirements:
|
Requirements:
|
||||||
|
|
||||||
* Python (2.5, 2.6, 2.7 supported)
|
* Python (2.5, 2.6, 2.7 supported)
|
||||||
* Django (1.2, 1.3, 1.4-alpha supported)
|
* Django (1.2, 1.3, 1.4 supported)
|
||||||
|
|
||||||
|
|
||||||
Installation Notes
|
Installation Notes
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
__version__ = '0.3.3'
|
__version__ = '0.4.0-dev'
|
||||||
|
|
||||||
VERSION = __version__ # synonym
|
VERSION = __version__ # synonym
|
||||||
|
|
|
@ -65,15 +65,45 @@ except ImportError:
|
||||||
environ.update(request)
|
environ.update(request)
|
||||||
return WSGIRequest(environ)
|
return WSGIRequest(environ)
|
||||||
|
|
||||||
# django.views.generic.View (Django >= 1.3)
|
# django.views.generic.View (1.3 <= Django < 1.4)
|
||||||
try:
|
try:
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
if not hasattr(View, 'head'):
|
|
||||||
|
if django.VERSION < (1, 4):
|
||||||
|
from django.utils.decorators import classonlymethod
|
||||||
|
from django.utils.functional import update_wrapper
|
||||||
|
|
||||||
# First implementation of Django class-based views did not include head method
|
# First implementation of Django class-based views did not include head method
|
||||||
# in base View class - https://code.djangoproject.com/ticket/15668
|
# in base View class - https://code.djangoproject.com/ticket/15668
|
||||||
class ViewPlusHead(View):
|
class ViewPlusHead(View):
|
||||||
def head(self, request, *args, **kwargs):
|
@classonlymethod
|
||||||
return self.get(request, *args, **kwargs)
|
def as_view(cls, **initkwargs):
|
||||||
|
"""
|
||||||
|
Main entry point for a request-response process.
|
||||||
|
"""
|
||||||
|
# sanitize keyword arguments
|
||||||
|
for key in initkwargs:
|
||||||
|
if key in cls.http_method_names:
|
||||||
|
raise TypeError(u"You tried to pass in the %s method name as a "
|
||||||
|
u"keyword argument to %s(). Don't do that."
|
||||||
|
% (key, cls.__name__))
|
||||||
|
if not hasattr(cls, key):
|
||||||
|
raise TypeError(u"%s() received an invalid keyword %r" % (
|
||||||
|
cls.__name__, key))
|
||||||
|
|
||||||
|
def view(request, *args, **kwargs):
|
||||||
|
self = cls(**initkwargs)
|
||||||
|
if hasattr(self, 'get') and not hasattr(self, 'head'):
|
||||||
|
self.head = self.get
|
||||||
|
return self.dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
# take name and docstring from class
|
||||||
|
update_wrapper(view, cls, updated=())
|
||||||
|
|
||||||
|
# and possible attributes set by decorators
|
||||||
|
# like csrf_exempt from dispatch
|
||||||
|
update_wrapper(view, cls.dispatch, assigned=())
|
||||||
|
return view
|
||||||
View = ViewPlusHead
|
View = ViewPlusHead
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -121,6 +151,8 @@ except ImportError:
|
||||||
|
|
||||||
def view(request, *args, **kwargs):
|
def view(request, *args, **kwargs):
|
||||||
self = cls(**initkwargs)
|
self = cls(**initkwargs)
|
||||||
|
if hasattr(self, 'get') and not hasattr(self, 'head'):
|
||||||
|
self.head = self.get
|
||||||
return self.dispatch(request, *args, **kwargs)
|
return self.dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
# take name and docstring from class
|
# take name and docstring from class
|
||||||
|
@ -154,9 +186,6 @@ except ImportError:
|
||||||
#)
|
#)
|
||||||
return http.HttpResponseNotAllowed(allowed_methods)
|
return http.HttpResponseNotAllowed(allowed_methods)
|
||||||
|
|
||||||
def head(self, request, *args, **kwargs):
|
|
||||||
return self.get(request, *args, **kwargs)
|
|
||||||
|
|
||||||
# PUT, DELETE do not require CSRF until 1.4. They should. Make it better.
|
# PUT, DELETE do not require CSRF until 1.4. They should. Make it better.
|
||||||
if django.VERSION >= (1, 4):
|
if django.VERSION >= (1, 4):
|
||||||
from django.middleware.csrf import CsrfViewMiddleware
|
from django.middleware.csrf import CsrfViewMiddleware
|
||||||
|
@ -370,6 +399,8 @@ else:
|
||||||
# Markdown is optional
|
# Markdown is optional
|
||||||
try:
|
try:
|
||||||
import markdown
|
import markdown
|
||||||
|
if markdown.version_info < (2, 0):
|
||||||
|
raise ImportError('Markdown < 2.0 is not supported.')
|
||||||
|
|
||||||
class CustomSetextHeaderProcessor(markdown.blockprocessors.BlockProcessor):
|
class CustomSetextHeaderProcessor(markdown.blockprocessors.BlockProcessor):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -181,7 +181,7 @@ class RequestMixin(object):
|
||||||
return parser.parse(stream)
|
return parser.parse(stream)
|
||||||
|
|
||||||
raise ErrorResponse(status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
|
raise ErrorResponse(status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
|
||||||
{'error': 'Unsupported media type in request \'%s\'.' %
|
{'detail': 'Unsupported media type in request \'%s\'.' %
|
||||||
content_type})
|
content_type})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -274,7 +274,8 @@ class ResponseMixin(object):
|
||||||
accept_list = [request.GET.get(self._ACCEPT_QUERY_PARAM)]
|
accept_list = [request.GET.get(self._ACCEPT_QUERY_PARAM)]
|
||||||
elif (self._IGNORE_IE_ACCEPT_HEADER and
|
elif (self._IGNORE_IE_ACCEPT_HEADER and
|
||||||
'HTTP_USER_AGENT' in request.META and
|
'HTTP_USER_AGENT' in request.META and
|
||||||
MSIE_USER_AGENT_REGEX.match(request.META['HTTP_USER_AGENT'])):
|
MSIE_USER_AGENT_REGEX.match(request.META['HTTP_USER_AGENT']) and
|
||||||
|
request.META.get('HTTP_X_REQUESTED_WITH', '') != 'XMLHttpRequest'):
|
||||||
# Ignore MSIE's broken accept behavior and do something sensible instead
|
# Ignore MSIE's broken accept behavior and do something sensible instead
|
||||||
accept_list = ['text/html', '*/*']
|
accept_list = ['text/html', '*/*']
|
||||||
elif 'HTTP_ACCEPT' in request.META:
|
elif 'HTTP_ACCEPT' in request.META:
|
||||||
|
|
|
@ -182,6 +182,10 @@ class TemplateRenderer(BaseRenderer):
|
||||||
media_type = None
|
media_type = None
|
||||||
template = None
|
template = None
|
||||||
|
|
||||||
|
def __init__(self, view):
|
||||||
|
super(TemplateRenderer, self).__init__(view)
|
||||||
|
self.template = getattr(self.view, "template", self.template)
|
||||||
|
|
||||||
def render(self, obj=None, media_type=None):
|
def render(self, obj=None, media_type=None):
|
||||||
"""
|
"""
|
||||||
Renders *obj* using the :attr:`template` specified on the class.
|
Renders *obj* using the :attr:`template` specified on the class.
|
||||||
|
@ -202,6 +206,10 @@ class DocumentingTemplateRenderer(BaseRenderer):
|
||||||
|
|
||||||
template = None
|
template = None
|
||||||
|
|
||||||
|
def __init__(self, view):
|
||||||
|
super(DocumentingTemplateRenderer, self).__init__(view)
|
||||||
|
self.template = getattr(self.view, "template", self.template)
|
||||||
|
|
||||||
def _get_content(self, view, request, obj, media_type):
|
def _get_content(self, view, request, obj, media_type):
|
||||||
"""
|
"""
|
||||||
Get the content as if it had been rendered by a non-documenting renderer.
|
Get the content as if it had been rendered by a non-documenting renderer.
|
||||||
|
|
|
@ -169,8 +169,9 @@ class FormResource(Resource):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add any unknown field errors
|
# Add any unknown field errors
|
||||||
for key in unknown_fields:
|
if not self.allow_unknown_form_fields:
|
||||||
field_errors[key] = [u'This field does not exist.']
|
for key in unknown_fields:
|
||||||
|
field_errors[key] = [u'This field does not exist.']
|
||||||
|
|
||||||
if field_errors:
|
if field_errors:
|
||||||
detail[u'field_errors'] = field_errors
|
detail[u'field_errors'] = field_errors
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
Customizable serialization.
|
Customizable serialization.
|
||||||
"""
|
"""
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet, RawQuerySet
|
||||||
from django.utils.encoding import smart_unicode, is_protected_type, smart_str
|
from django.utils.encoding import smart_unicode, is_protected_type, smart_str
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
|
@ -25,16 +25,9 @@ def _field_to_tuple(field):
|
||||||
|
|
||||||
def _fields_to_list(fields):
|
def _fields_to_list(fields):
|
||||||
"""
|
"""
|
||||||
Return a list of field names.
|
Return a list of field tuples.
|
||||||
"""
|
"""
|
||||||
return [_field_to_tuple(field)[0] for field in fields or ()]
|
return [_field_to_tuple(field) for field in fields or ()]
|
||||||
|
|
||||||
|
|
||||||
def _fields_to_dict(fields):
|
|
||||||
"""
|
|
||||||
Return a `dict` of field name -> None, or tuple of fields, or Serializer class
|
|
||||||
"""
|
|
||||||
return dict([_field_to_tuple(field) for field in fields or ()])
|
|
||||||
|
|
||||||
|
|
||||||
class _SkipField(Exception):
|
class _SkipField(Exception):
|
||||||
|
@ -103,6 +96,11 @@ class Serializer(object):
|
||||||
"""
|
"""
|
||||||
The maximum depth to serialize to, or `None`.
|
The maximum depth to serialize to, or `None`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
parent = None
|
||||||
|
"""
|
||||||
|
A reference to the root serializer when descending down into fields.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, depth=None, stack=[], **kwargs):
|
def __init__(self, depth=None, stack=[], **kwargs):
|
||||||
if depth is not None:
|
if depth is not None:
|
||||||
|
@ -110,9 +108,6 @@ class Serializer(object):
|
||||||
self.stack = stack
|
self.stack = stack
|
||||||
|
|
||||||
def get_fields(self, obj):
|
def get_fields(self, obj):
|
||||||
"""
|
|
||||||
Return the set of field names/keys to use for a model instance/dict.
|
|
||||||
"""
|
|
||||||
fields = self.fields
|
fields = self.fields
|
||||||
|
|
||||||
# If `fields` is not set, we use the default fields and modify
|
# If `fields` is not set, we use the default fields and modify
|
||||||
|
@ -123,9 +118,6 @@ class Serializer(object):
|
||||||
exclude = self.exclude or ()
|
exclude = self.exclude or ()
|
||||||
fields = set(default + list(include)) - set(exclude)
|
fields = set(default + list(include)) - set(exclude)
|
||||||
|
|
||||||
else:
|
|
||||||
fields = _fields_to_list(self.fields)
|
|
||||||
|
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
def get_default_fields(self, obj):
|
def get_default_fields(self, obj):
|
||||||
|
@ -139,15 +131,16 @@ class Serializer(object):
|
||||||
else:
|
else:
|
||||||
return obj.keys()
|
return obj.keys()
|
||||||
|
|
||||||
def get_related_serializer(self, key):
|
def get_related_serializer(self, info):
|
||||||
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
|
||||||
|
|
||||||
|
class OnTheFlySerializer(self.__class__):
|
||||||
|
fields = info
|
||||||
|
parent = getattr(self, 'parent') or self
|
||||||
|
|
||||||
if isinstance(info, (list, tuple)):
|
if isinstance(info, (list, tuple)):
|
||||||
class OnTheFlySerializer(self.__class__):
|
|
||||||
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)
|
||||||
|
@ -165,8 +158,9 @@ class Serializer(object):
|
||||||
elif isinstance(info, str) and info in _serializers:
|
elif isinstance(info, str) and info in _serializers:
|
||||||
return _serializers[info]
|
return _serializers[info]
|
||||||
|
|
||||||
# Otherwise use `related_serializer` or fall back to `Serializer`
|
# Otherwise use `related_serializer` or fall back to
|
||||||
return getattr(self, 'related_serializer') or Serializer
|
# `OnTheFlySerializer` preserve custom serialization methods.
|
||||||
|
return getattr(self, 'related_serializer') or OnTheFlySerializer
|
||||||
|
|
||||||
def serialize_key(self, key):
|
def serialize_key(self, key):
|
||||||
"""
|
"""
|
||||||
|
@ -175,11 +169,11 @@ class Serializer(object):
|
||||||
"""
|
"""
|
||||||
return self.rename.get(smart_str(key), smart_str(key))
|
return self.rename.get(smart_str(key), smart_str(key))
|
||||||
|
|
||||||
def serialize_val(self, key, obj):
|
def serialize_val(self, key, obj, related_info):
|
||||||
"""
|
"""
|
||||||
Convert a model field or dict value into a serializable representation.
|
Convert a model field or dict value into a serializable representation.
|
||||||
"""
|
"""
|
||||||
related_serializer = self.get_related_serializer(key)
|
related_serializer = self.get_related_serializer(related_info)
|
||||||
|
|
||||||
if self.depth is None:
|
if self.depth is None:
|
||||||
depth = None
|
depth = None
|
||||||
|
@ -194,7 +188,8 @@ class Serializer(object):
|
||||||
stack = self.stack[:]
|
stack = self.stack[:]
|
||||||
stack.append(obj)
|
stack.append(obj)
|
||||||
|
|
||||||
return related_serializer(depth=depth, stack=stack).serialize(obj)
|
return related_serializer(depth=depth, stack=stack).serialize(
|
||||||
|
obj, request=getattr(self, 'request', None))
|
||||||
|
|
||||||
def serialize_max_depth(self, obj):
|
def serialize_max_depth(self, obj):
|
||||||
"""
|
"""
|
||||||
|
@ -219,7 +214,7 @@ class Serializer(object):
|
||||||
fields = self.get_fields(instance)
|
fields = self.get_fields(instance)
|
||||||
|
|
||||||
# serialize each required field
|
# serialize each required field
|
||||||
for fname in fields:
|
for fname, related_info in _fields_to_list(fields):
|
||||||
try:
|
try:
|
||||||
# we first check for a method 'fname' on self,
|
# we first check for a method 'fname' on self,
|
||||||
# 'fname's signature must be 'def fname(self, instance)'
|
# 'fname's signature must be 'def fname(self, instance)'
|
||||||
|
@ -237,7 +232,7 @@ class Serializer(object):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
key = self.serialize_key(fname)
|
key = self.serialize_key(fname)
|
||||||
val = self.serialize_val(fname, obj)
|
val = self.serialize_val(fname, obj, related_info)
|
||||||
data[key] = val
|
data[key] = val
|
||||||
except _SkipField:
|
except _SkipField:
|
||||||
pass
|
pass
|
||||||
|
@ -268,15 +263,19 @@ class Serializer(object):
|
||||||
"""
|
"""
|
||||||
return smart_unicode(obj, strings_only=True)
|
return smart_unicode(obj, strings_only=True)
|
||||||
|
|
||||||
def serialize(self, obj):
|
def serialize(self, obj, request=None):
|
||||||
"""
|
"""
|
||||||
Convert any object into a serializable representation.
|
Convert any object into a serializable representation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Request from related serializer.
|
||||||
|
if request is not None:
|
||||||
|
self.request = request
|
||||||
|
|
||||||
if isinstance(obj, (dict, models.Model)):
|
if isinstance(obj, (dict, models.Model)):
|
||||||
# Model instances & dictionaries
|
# Model instances & dictionaries
|
||||||
return self.serialize_model(obj)
|
return self.serialize_model(obj)
|
||||||
elif isinstance(obj, (tuple, list, set, QuerySet, types.GeneratorType)):
|
elif isinstance(obj, (tuple, list, set, QuerySet, RawQuerySet, types.GeneratorType)):
|
||||||
# basic iterables
|
# basic iterables
|
||||||
return self.serialize_iter(obj)
|
return self.serialize_iter(obj)
|
||||||
elif isinstance(obj, models.Manager):
|
elif isinstance(obj, models.Manager):
|
||||||
|
|
|
@ -4,7 +4,7 @@ register = Library()
|
||||||
|
|
||||||
|
|
||||||
def add_query_param(url, param):
|
def add_query_param(url, param):
|
||||||
return unicode(URLObject(url).with_query(param))
|
return unicode(URLObject(url).add_query_param(*param.split('=')))
|
||||||
|
|
||||||
|
|
||||||
register.filter('add_query_param', add_query_param)
|
register.filter('add_query_param', add_query_param)
|
|
@ -50,6 +50,16 @@ class UserAgentMungingTest(TestCase):
|
||||||
resp = self.view(req)
|
resp = self.view(req)
|
||||||
self.assertEqual(resp['Content-Type'], 'text/html')
|
self.assertEqual(resp['Content-Type'], 'text/html')
|
||||||
|
|
||||||
|
def test_dont_munge_msie_with_x_requested_with_header(self):
|
||||||
|
"""Send MSIE user agent strings, and an X-Requested-With header, and
|
||||||
|
ensure that we get a JSON response if we set a */* Accept header."""
|
||||||
|
for user_agent in (MSIE_9_USER_AGENT,
|
||||||
|
MSIE_8_USER_AGENT,
|
||||||
|
MSIE_7_USER_AGENT):
|
||||||
|
req = self.req.get('/', HTTP_ACCEPT='*/*', HTTP_USER_AGENT=user_agent, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||||
|
resp = self.view(req)
|
||||||
|
self.assertEqual(resp['Content-Type'], 'application/json')
|
||||||
|
|
||||||
def test_dont_rewrite_msie_accept_header(self):
|
def test_dont_rewrite_msie_accept_header(self):
|
||||||
"""Turn off _IGNORE_IE_ACCEPT_HEADER, send MSIE user agent strings and ensure
|
"""Turn off _IGNORE_IE_ACCEPT_HEADER, send MSIE user agent strings and ensure
|
||||||
that we get a JSON response if we set a */* accept header."""
|
that we get a JSON response if we set a */* accept header."""
|
||||||
|
|
|
@ -104,6 +104,27 @@ class TestFieldNesting(TestCase):
|
||||||
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'}})
|
||||||
|
|
||||||
|
def test_serializer_no_fields(self):
|
||||||
|
"""
|
||||||
|
Test related serializer works when the fields attr isn't present. Fix for
|
||||||
|
#178.
|
||||||
|
"""
|
||||||
|
class NestedM2(Serializer):
|
||||||
|
fields = ('field1', )
|
||||||
|
|
||||||
|
class NestedM3(Serializer):
|
||||||
|
fields = ('field2', )
|
||||||
|
|
||||||
|
class SerializerM2(Serializer):
|
||||||
|
include = [('field', NestedM2)]
|
||||||
|
exclude = ('id', )
|
||||||
|
|
||||||
|
class SerializerM3(Serializer):
|
||||||
|
fields = [('field', NestedM3)]
|
||||||
|
|
||||||
|
self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
|
||||||
|
self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
|
||||||
|
|
||||||
def test_serializer_classname_nesting(self):
|
def test_serializer_classname_nesting(self):
|
||||||
"""
|
"""
|
||||||
Test related model serialization
|
Test related model serialization
|
||||||
|
|
|
@ -156,6 +156,9 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
|
||||||
|
|
||||||
description = _remove_leading_indent(description)
|
description = _remove_leading_indent(description)
|
||||||
|
|
||||||
|
if not isinstance(description, unicode):
|
||||||
|
description = description.decode('UTF-8')
|
||||||
|
|
||||||
if html:
|
if html:
|
||||||
return self.markup_description(description)
|
return self.markup_description(description)
|
||||||
return description
|
return description
|
||||||
|
|
|
@ -7,7 +7,7 @@ Alternative frameworks
|
||||||
There are a number of alternative REST frameworks for Django:
|
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-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.
|
* `django-tastypie <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/>`_
|
* Other interesting projects include `dagny <https://github.com/zacharyvoase/dagny>`_ and `dj-webmachine <http://benoitc.github.com/dj-webmachine/>`_
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Documentation requires Django & Sphinx, and their dependencies...
|
# Documentation requires Django & Sphinx, and their dependencies...
|
||||||
|
|
||||||
Django==1.2.4
|
Django>=1.2.4
|
||||||
Jinja2==2.5.5
|
Jinja2==2.5.5
|
||||||
Pygments==1.4
|
Pygments==1.4
|
||||||
Sphinx==1.0.7
|
Sphinx==1.0.7
|
||||||
|
|
|
@ -12,11 +12,11 @@ class PermissionsExampleView(View):
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
'name': 'Throttling Example',
|
'name': 'Throttling Example',
|
||||||
'url': reverse('throttled-resource', request)
|
'url': reverse('throttled-resource', request=request)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Logged in example',
|
'name': 'Logged in example',
|
||||||
'url': reverse('loggedin-resource', request)
|
'url': reverse('loggedin-resource', request=request)
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ class PygmentsRoot(View):
|
||||||
Return a list of all currently existing snippets.
|
Return a list of all currently existing snippets.
|
||||||
"""
|
"""
|
||||||
unique_ids = [os.path.split(f)[1] for f in list_dir_sorted_by_ctime(HIGHLIGHTED_CODE_DIR)]
|
unique_ids = [os.path.split(f)[1] for f in list_dir_sorted_by_ctime(HIGHLIGHTED_CODE_DIR)]
|
||||||
return [reverse('pygments-instance', request, args=[unique_id]) for unique_id in unique_ids]
|
return [reverse('pygments-instance', request=request, args=[unique_id]) for unique_id in unique_ids]
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""
|
"""
|
||||||
|
@ -85,7 +85,7 @@ class PygmentsRoot(View):
|
||||||
|
|
||||||
remove_oldest_files(HIGHLIGHTED_CODE_DIR, MAX_FILES)
|
remove_oldest_files(HIGHLIGHTED_CODE_DIR, MAX_FILES)
|
||||||
|
|
||||||
return Response(status.HTTP_201_CREATED, headers={'Location': reverse('pygments-instance', request, args=[unique_id])})
|
return Response(status.HTTP_201_CREATED, headers={'Location': reverse('pygments-instance', request=request, args=[unique_id])})
|
||||||
|
|
||||||
|
|
||||||
class PygmentsInstance(View):
|
class PygmentsInstance(View):
|
||||||
|
|
3
setup.py
3
setup.py
|
@ -64,6 +64,9 @@ setup(
|
||||||
package_data=get_package_data('djangorestframework'),
|
package_data=get_package_data('djangorestframework'),
|
||||||
test_suite='djangorestframework.runtests.runcoverage.main',
|
test_suite='djangorestframework.runtests.runcoverage.main',
|
||||||
install_requires=['URLObject>=0.6.0'],
|
install_requires=['URLObject>=0.6.0'],
|
||||||
|
extras_require={
|
||||||
|
'markdown': ["Markdown>=2.0"]
|
||||||
|
},
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 4 - Beta',
|
'Development Status :: 4 - Beta',
|
||||||
'Environment :: Web Environment',
|
'Environment :: Web Environment',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user