mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-03-25 04:14:23 +03:00
reverse takes request as a kwarg for compatibility with django's reverse
This commit is contained in:
parent
8e0b9e55ec
commit
2b59df004a
|
@ -214,18 +214,15 @@ else:
|
||||||
REASON_NO_CSRF_COOKIE = "CSRF cookie not set."
|
REASON_NO_CSRF_COOKIE = "CSRF cookie not set."
|
||||||
REASON_BAD_TOKEN = "CSRF token missing or incorrect."
|
REASON_BAD_TOKEN = "CSRF token missing or incorrect."
|
||||||
|
|
||||||
|
|
||||||
def _get_failure_view():
|
def _get_failure_view():
|
||||||
"""
|
"""
|
||||||
Returns the view to be used for CSRF rejections
|
Returns the view to be used for CSRF rejections
|
||||||
"""
|
"""
|
||||||
return get_callable(settings.CSRF_FAILURE_VIEW)
|
return get_callable(settings.CSRF_FAILURE_VIEW)
|
||||||
|
|
||||||
|
|
||||||
def _get_new_csrf_key():
|
def _get_new_csrf_key():
|
||||||
return hashlib.md5("%s%s" % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
|
return hashlib.md5("%s%s" % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def get_token(request):
|
def get_token(request):
|
||||||
"""
|
"""
|
||||||
Returns the the CSRF token required for a POST form. The token is an
|
Returns the the CSRF token required for a POST form. The token is an
|
||||||
|
@ -239,7 +236,6 @@ else:
|
||||||
request.META["CSRF_COOKIE_USED"] = True
|
request.META["CSRF_COOKIE_USED"] = True
|
||||||
return request.META.get("CSRF_COOKIE", None)
|
return request.META.get("CSRF_COOKIE", None)
|
||||||
|
|
||||||
|
|
||||||
def _sanitize_token(token):
|
def _sanitize_token(token):
|
||||||
# Allow only alphanum, and ensure we return a 'str' for the sake of the post
|
# Allow only alphanum, and ensure we return a 'str' for the sake of the post
|
||||||
# processing middleware.
|
# processing middleware.
|
||||||
|
@ -432,12 +428,13 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
yaml = None
|
yaml = None
|
||||||
|
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
try:
|
try:
|
||||||
import unittest.skip
|
import unittest.skip
|
||||||
except ImportError: # python < 2.7
|
except ImportError: # python < 2.7
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
def skip(reason):
|
def skip(reason):
|
||||||
# Pasted from py27/lib/unittest/case.py
|
# Pasted from py27/lib/unittest/case.py
|
||||||
|
@ -448,26 +445,19 @@ except ImportError: # python < 2.7
|
||||||
if not (isinstance(test_item, type) and issubclass(test_item, TestCase)):
|
if not (isinstance(test_item, type) and issubclass(test_item, TestCase)):
|
||||||
@functools.wraps(test_item)
|
@functools.wraps(test_item)
|
||||||
def skip_wrapper(*args, **kwargs):
|
def skip_wrapper(*args, **kwargs):
|
||||||
pass
|
pass
|
||||||
test_item = skip_wrapper
|
test_item = skip_wrapper
|
||||||
|
|
||||||
test_item.__unittest_skip__ = True
|
test_item.__unittest_skip__ = True
|
||||||
test_item.__unittest_skip_why__ = reason
|
test_item.__unittest_skip_why__ = reason
|
||||||
return test_item
|
return test_item
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
unittest.skip = skip
|
unittest.skip = skip
|
||||||
|
|
||||||
# reverse_lazy (Django 1.4 onwards)
|
|
||||||
try:
|
|
||||||
from django.core.urlresolvers import reverse_lazy
|
|
||||||
except:
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.utils.functional import lazy
|
|
||||||
reverse_lazy = lazy(reverse, str)
|
|
||||||
|
|
||||||
# xml.etree.parse only throws ParseError for python >= 2.7
|
# xml.etree.parse only throws ParseError for python >= 2.7
|
||||||
try:
|
try:
|
||||||
from xml.etree import ParseError as ETParseError
|
from xml.etree import ParseError as ETParseError
|
||||||
except ImportError: # python < 2.7
|
except ImportError: # python < 2.7
|
||||||
ETParseError = None
|
ETParseError = None
|
||||||
|
|
|
@ -10,7 +10,8 @@ from djangorestframework.utils import as_tuple
|
||||||
|
|
||||||
class BaseResource(Serializer):
|
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.
|
||||||
"""
|
"""
|
||||||
fields = None
|
fields = None
|
||||||
include = None
|
include = None
|
||||||
|
@ -19,11 +20,13 @@ class BaseResource(Serializer):
|
||||||
def __init__(self, view=None, depth=None, stack=[], **kwargs):
|
def __init__(self, view=None, depth=None, stack=[], **kwargs):
|
||||||
super(BaseResource, self).__init__(depth, stack, **kwargs)
|
super(BaseResource, self).__init__(depth, stack, **kwargs)
|
||||||
self.view = view
|
self.view = view
|
||||||
|
self.request = view.request
|
||||||
|
|
||||||
def validate_request(self, data, files=None):
|
def validate_request(self, data, files=None):
|
||||||
"""
|
"""
|
||||||
Given the request content return the cleaned, validated content.
|
Given the request content return the cleaned, validated content.
|
||||||
Typically raises a :exc:`response.ErrorResponse` with status code 400 (Bad Request) on failure.
|
Typically raises a :exc:`response.ErrorResponse` with status code 400
|
||||||
|
(Bad Request) on failure.
|
||||||
"""
|
"""
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@ -37,7 +40,8 @@ class BaseResource(Serializer):
|
||||||
class Resource(BaseResource):
|
class Resource(BaseResource):
|
||||||
"""
|
"""
|
||||||
A Resource determines how a python object maps to some serializable data.
|
A Resource determines how a python object maps to some serializable data.
|
||||||
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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 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.
|
||||||
|
@ -355,7 +359,9 @@ class ModelResource(FormResource):
|
||||||
instance_attrs[param] = attr
|
instance_attrs[param] = attr
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return reverse(self.view_callable[0], self.view.request, kwargs=instance_attrs)
|
return reverse(self.view_callable[0],
|
||||||
|
kwargs=instance_attrs,
|
||||||
|
request=self.view.request)
|
||||||
except NoReverseMatch:
|
except NoReverseMatch:
|
||||||
pass
|
pass
|
||||||
raise _SkipField
|
raise _SkipField
|
||||||
|
|
|
@ -2,22 +2,19 @@
|
||||||
Provide reverse functions that return fully qualified URLs
|
Provide reverse functions that return fully qualified URLs
|
||||||
"""
|
"""
|
||||||
from django.core.urlresolvers import reverse as django_reverse
|
from django.core.urlresolvers import reverse as django_reverse
|
||||||
from djangorestframework.compat import reverse_lazy as django_reverse_lazy
|
from django.utils.functional import lazy
|
||||||
|
|
||||||
|
|
||||||
def reverse(viewname, request, *args, **kwargs):
|
def reverse(viewname, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Do the same as `django.core.urlresolvers.reverse` but using
|
Same as `django.core.urlresolvers.reverse`, but optionally takes a request
|
||||||
*request* to build a fully qualified URL.
|
and returns a fully qualified URL, using the request to get the base URL.
|
||||||
"""
|
"""
|
||||||
|
request = kwargs.pop('request', None)
|
||||||
url = django_reverse(viewname, *args, **kwargs)
|
url = django_reverse(viewname, *args, **kwargs)
|
||||||
return request.build_absolute_uri(url)
|
if request:
|
||||||
|
return request.build_absolute_uri(url)
|
||||||
|
return url
|
||||||
|
|
||||||
|
|
||||||
def reverse_lazy(viewname, request, *args, **kwargs):
|
reverse_lazy = lazy(reverse, str)
|
||||||
"""
|
|
||||||
Do the same as `django.core.urlresolvers.reverse_lazy` but using
|
|
||||||
*request* to build a fully qualified URL.
|
|
||||||
"""
|
|
||||||
url = django_reverse_lazy(viewname, *args, **kwargs)
|
|
||||||
return request.build_absolute_uri(url)
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ class MyView(View):
|
||||||
renderers = (JSONRenderer, )
|
renderers = (JSONRenderer, )
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
return reverse('myview', request)
|
return reverse('myview', request=request)
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^myview$', MyView.as_view(), name='myview'),
|
url(r'^myview$', MyView.as_view(), name='myview'),
|
||||||
|
|
|
@ -12,7 +12,9 @@ class BlogPostResource(ModelResource):
|
||||||
ordering = ('-created',)
|
ordering = ('-created',)
|
||||||
|
|
||||||
def comments(self, instance):
|
def comments(self, instance):
|
||||||
return reverse('comments', request, kwargs={'blogpost': instance.key})
|
return reverse('comments',
|
||||||
|
kwargs={'blogpost': instance.key},
|
||||||
|
request=self.request)
|
||||||
|
|
||||||
|
|
||||||
class CommentResource(ModelResource):
|
class CommentResource(ModelResource):
|
||||||
|
@ -24,4 +26,6 @@ class CommentResource(ModelResource):
|
||||||
ordering = ('-created',)
|
ordering = ('-created',)
|
||||||
|
|
||||||
def blogpost(self, instance):
|
def blogpost(self, instance):
|
||||||
return reverse('blog-post', request, kwargs={'key': instance.blogpost.key})
|
return reverse('blog-post',
|
||||||
|
kwargs={'key': instance.blogpost.key},
|
||||||
|
request=self.request)
|
||||||
|
|
|
@ -9,16 +9,17 @@ from django.conf.urls.defaults import patterns, url
|
||||||
|
|
||||||
class ExampleView(ResponseMixin, View):
|
class ExampleView(ResponseMixin, View):
|
||||||
"""An example view using Django 1.3's class based views.
|
"""An example view using Django 1.3's class based views.
|
||||||
Uses djangorestframework's RendererMixin to provide support for multiple output formats."""
|
Uses djangorestframework's RendererMixin to provide support for multiple
|
||||||
|
output formats."""
|
||||||
renderers = DEFAULT_RENDERERS
|
renderers = DEFAULT_RENDERERS
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
url = reverse('mixin-view', request=request)
|
||||||
response = Response(200, {'description': 'Some example content',
|
response = Response(200, {'description': 'Some example content',
|
||||||
'url': reverse('mixin-view', request)})
|
'url': url})
|
||||||
return self.render(response)
|
return self.render(response)
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^$', ExampleView.as_view(), name='mixin-view'),
|
url(r'^$', ExampleView.as_view(), name='mixin-view'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.db import models
|
||||||
|
|
||||||
MAX_INSTANCES = 10
|
MAX_INSTANCES = 10
|
||||||
|
|
||||||
|
|
||||||
class MyModel(models.Model):
|
class MyModel(models.Model):
|
||||||
foo = models.BooleanField()
|
foo = models.BooleanField()
|
||||||
bar = models.IntegerField(help_text='Must be an integer.')
|
bar = models.IntegerField(help_text='Must be an integer.')
|
||||||
|
@ -15,5 +16,3 @@ class MyModel(models.Model):
|
||||||
super(MyModel, self).save(*args, **kwargs)
|
super(MyModel, self).save(*args, **kwargs)
|
||||||
while MyModel.objects.all().count() > MAX_INSTANCES:
|
while MyModel.objects.all().count() > MAX_INSTANCES:
|
||||||
MyModel.objects.all().order_by('-created')[0].delete()
|
MyModel.objects.all().order_by('-created')[0].delete()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from djangorestframework.resources import ModelResource
|
from djangorestframework.resources import ModelResource
|
||||||
from modelresourceexample.models import MyModel
|
from modelresourceexample.models import MyModel
|
||||||
|
|
||||||
|
|
||||||
class MyModelResource(ModelResource):
|
class MyModelResource(ModelResource):
|
||||||
model = MyModel
|
model = MyModel
|
||||||
fields = ('foo', 'bar', 'baz', 'url')
|
fields = ('foo', 'bar', 'baz', 'url')
|
||||||
|
|
|
@ -4,5 +4,5 @@ from modelresourceexample.resources import MyModelResource
|
||||||
|
|
||||||
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'^(?P<pk>[0-9]+)/$', InstanceModelView.as_view(resource=MyModelResource)),
|
url(r'^(?P<id>[0-9]+)/$', InstanceModelView.as_view(resource=MyModelResource), name='model-resource-instance'),
|
||||||
)
|
)
|
||||||
|
|
|
@ -28,6 +28,20 @@ def remove_oldest_files(dir, max_files):
|
||||||
[os.remove(path) for path in ctime_sorted_paths[max_files:]]
|
[os.remove(path) for path in ctime_sorted_paths[max_files:]]
|
||||||
|
|
||||||
|
|
||||||
|
def get_filename(key):
|
||||||
|
"""
|
||||||
|
Given a stored object's key returns the file's path.
|
||||||
|
"""
|
||||||
|
return os.path.join(OBJECT_STORE_DIR, key)
|
||||||
|
|
||||||
|
|
||||||
|
def get_file_url(key, request):
|
||||||
|
"""
|
||||||
|
Given a stored object's key returns the URL for the object.
|
||||||
|
"""
|
||||||
|
return reverse('stored-object', kwargs={'key': key}, request=request)
|
||||||
|
|
||||||
|
|
||||||
class ObjectStoreRoot(View):
|
class ObjectStoreRoot(View):
|
||||||
"""
|
"""
|
||||||
Root of the Object Store API.
|
Root of the Object Store API.
|
||||||
|
@ -38,20 +52,24 @@ class ObjectStoreRoot(View):
|
||||||
"""
|
"""
|
||||||
Return a list of all the stored object URLs. (Ordered by creation time, newest first)
|
Return a list of all the stored object URLs. (Ordered by creation time, newest first)
|
||||||
"""
|
"""
|
||||||
filepaths = [os.path.join(OBJECT_STORE_DIR, file) for file in os.listdir(OBJECT_STORE_DIR) if not file.startswith('.')]
|
filepaths = [os.path.join(OBJECT_STORE_DIR, file)
|
||||||
|
for file in os.listdir(OBJECT_STORE_DIR)
|
||||||
|
if not file.startswith('.')]
|
||||||
ctime_sorted_basenames = [item[0] for item in sorted([(os.path.basename(path), os.path.getctime(path)) for path in filepaths],
|
ctime_sorted_basenames = [item[0] for item in sorted([(os.path.basename(path), os.path.getctime(path)) for path in filepaths],
|
||||||
key=operator.itemgetter(1), reverse=True)]
|
key=operator.itemgetter(1), reverse=True)]
|
||||||
return [reverse('stored-object', request, kwargs={'key':key}) for key in ctime_sorted_basenames]
|
return [get_file_url(key, request) for key in ctime_sorted_basenames]
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""
|
"""
|
||||||
Create a new stored object, with a unique key.
|
Create a new stored object, with a unique key.
|
||||||
"""
|
"""
|
||||||
key = str(uuid.uuid1())
|
key = str(uuid.uuid1())
|
||||||
pathname = os.path.join(OBJECT_STORE_DIR, key)
|
filename = get_filename(key)
|
||||||
pickle.dump(self.CONTENT, open(pathname, 'wb'))
|
pickle.dump(self.CONTENT, open(filename, 'wb'))
|
||||||
|
|
||||||
remove_oldest_files(OBJECT_STORE_DIR, MAX_FILES)
|
remove_oldest_files(OBJECT_STORE_DIR, MAX_FILES)
|
||||||
return Response(status.HTTP_201_CREATED, self.CONTENT, {'Location': reverse('stored-object', request, kwargs={'key':key})})
|
url = get_file_url(key, request)
|
||||||
|
return Response(status.HTTP_201_CREATED, self.CONTENT, {'Location': url})
|
||||||
|
|
||||||
|
|
||||||
class StoredObject(View):
|
class StoredObject(View):
|
||||||
|
@ -59,29 +77,30 @@ class StoredObject(View):
|
||||||
Represents a stored object.
|
Represents a stored object.
|
||||||
The object may be any picklable content.
|
The object may be any picklable content.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get(self, request, key):
|
def get(self, request, key):
|
||||||
"""
|
"""
|
||||||
Return a stored object, by unpickling the contents of a locally stored file.
|
Return a stored object, by unpickling the contents of a locally
|
||||||
|
stored file.
|
||||||
"""
|
"""
|
||||||
pathname = os.path.join(OBJECT_STORE_DIR, key)
|
filename = get_filename(key)
|
||||||
if not os.path.exists(pathname):
|
if not os.path.exists(filename):
|
||||||
return Response(status.HTTP_404_NOT_FOUND)
|
return Response(status.HTTP_404_NOT_FOUND)
|
||||||
return pickle.load(open(pathname, 'rb'))
|
return pickle.load(open(filename, 'rb'))
|
||||||
|
|
||||||
def put(self, request, key):
|
def put(self, request, key):
|
||||||
"""
|
"""
|
||||||
Update/create a stored object, by pickling the request content to a locally stored file.
|
Update/create a stored object, by pickling the request content to a
|
||||||
|
locally stored file.
|
||||||
"""
|
"""
|
||||||
pathname = os.path.join(OBJECT_STORE_DIR, key)
|
filename = get_filename(key)
|
||||||
pickle.dump(self.CONTENT, open(pathname, 'wb'))
|
pickle.dump(self.CONTENT, open(filename, 'wb'))
|
||||||
return self.CONTENT
|
return self.CONTENT
|
||||||
|
|
||||||
def delete(self, request, key):
|
def delete(self, request, key):
|
||||||
"""
|
"""
|
||||||
Delete a stored object, by removing it's pickled file.
|
Delete a stored object, by removing it's pickled file.
|
||||||
"""
|
"""
|
||||||
pathname = os.path.join(OBJECT_STORE_DIR, key)
|
filename = get_filename(key)
|
||||||
if not os.path.exists(pathname):
|
if not os.path.exists(filename):
|
||||||
return Response(status.HTTP_404_NOT_FOUND)
|
return Response(status.HTTP_404_NOT_FOUND)
|
||||||
os.remove(pathname)
|
os.remove(filename)
|
||||||
|
|
|
@ -30,9 +30,13 @@ def list_dir_sorted_by_ctime(dir):
|
||||||
"""
|
"""
|
||||||
Return a list of files sorted by creation time
|
Return a list of files sorted by creation time
|
||||||
"""
|
"""
|
||||||
filepaths = [os.path.join(dir, file) for file in os.listdir(dir) if not file.startswith('.')]
|
filepaths = [os.path.join(dir, file)
|
||||||
return [item[0] for item in sorted( [(path, os.path.getctime(path)) for path in filepaths],
|
for file in os.listdir(dir)
|
||||||
key=operator.itemgetter(1), reverse=False) ]
|
if not file.startswith('.')]
|
||||||
|
ctimes = [(path, os.path.getctime(path)) for path in filepaths]
|
||||||
|
ctimes = sorted(ctimes, key=operator.itemgetter(1), reverse=False)
|
||||||
|
return [filepath for filepath, ctime in ctimes]
|
||||||
|
|
||||||
|
|
||||||
def remove_oldest_files(dir, max_files):
|
def remove_oldest_files(dir, max_files):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -15,7 +15,7 @@ class ExampleView(View):
|
||||||
"""
|
"""
|
||||||
Handle GET requests, returning a list of URLs pointing to 3 other views.
|
Handle GET requests, returning a list of URLs pointing to 3 other views.
|
||||||
"""
|
"""
|
||||||
return {"Some other resources": [reverse('another-example', request, kwargs={'num':num}) for num in range(3)]}
|
return {"Some other resources": [reverse('another-example', kwargs={'num':num}, request=request) for num in range(3)]}
|
||||||
|
|
||||||
|
|
||||||
class AnotherExampleView(View):
|
class AnotherExampleView(View):
|
||||||
|
|
|
@ -27,11 +27,11 @@ class Sandbox(View):
|
||||||
Please feel free to browse, create, edit and delete the resources in these examples."""
|
Please feel free to browse, create, edit and delete the resources in these examples."""
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
return [{'name': 'Simple Resource example', 'url': reverse('example-resource', request)},
|
return [{'name': 'Simple Resource example', 'url': reverse('example-resource', request=request)},
|
||||||
{'name': 'Simple ModelResource example', 'url': reverse('model-resource-root', request)},
|
{'name': 'Simple ModelResource example', 'url': reverse('model-resource-root', request=request)},
|
||||||
{'name': 'Simple Mixin-only example', 'url': reverse('mixin-view', request)},
|
{'name': 'Simple Mixin-only example', 'url': reverse('mixin-view', request=request)},
|
||||||
{'name': 'Object store API', 'url': reverse('object-store-root', request)},
|
{'name': 'Object store API', 'url': reverse('object-store-root', request=request)},
|
||||||
{'name': 'Code highlighting API', 'url': reverse('pygments-root', request)},
|
{'name': 'Code highlighting API', 'url': reverse('pygments-root', request=request)},
|
||||||
{'name': 'Blog posts API', 'url': reverse('blog-posts-root', request)},
|
{'name': 'Blog posts API', 'url': reverse('blog-posts-root', request=request)},
|
||||||
{'name': 'Permissions example', 'url': reverse('permissions-example', request)}
|
{'name': 'Permissions example', 'url': reverse('permissions-example', request=request)}
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user