mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-30 05:24:00 +03:00
Merge pull request #175 from izquierdo/custom_reverse
Custom reverse() and drop set_script_prefix
This commit is contained in:
commit
49ebaf106d
|
@ -1,10 +1,10 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.urlresolvers import reverse, get_urlconf, get_resolver, NoReverseMatch
|
from django.core.urlresolvers import get_urlconf, get_resolver, NoReverseMatch
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from djangorestframework.response import ErrorResponse
|
from djangorestframework.response import ErrorResponse
|
||||||
from djangorestframework.serializer import Serializer, _SkipField
|
from djangorestframework.serializer import Serializer, _SkipField
|
||||||
from djangorestframework.utils import as_tuple
|
from djangorestframework.utils import as_tuple, reverse
|
||||||
|
|
||||||
|
|
||||||
class BaseResource(Serializer):
|
class BaseResource(Serializer):
|
||||||
|
@ -354,7 +354,7 @@ class ModelResource(FormResource):
|
||||||
instance_attrs[param] = attr
|
instance_attrs[param] = attr
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return reverse(self.view_callable[0], kwargs=instance_attrs)
|
return reverse(self.view_callable[0], self.view.request, kwargs=instance_attrs)
|
||||||
except NoReverseMatch:
|
except NoReverseMatch:
|
||||||
pass
|
pass
|
||||||
raise _SkipField
|
raise _SkipField
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from django.conf.urls.defaults import patterns, url
|
from django.conf.urls.defaults import patterns, url
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils import simplejson as json
|
from django.utils import simplejson as json
|
||||||
|
|
||||||
|
from djangorestframework.utils import reverse
|
||||||
from djangorestframework.views import View
|
from djangorestframework.views import View
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ class MockView(View):
|
||||||
permissions = ()
|
permissions = ()
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
return reverse('another')
|
return reverse('another', request)
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^$', MockView.as_view()),
|
url(r'^$', MockView.as_view()),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
import django
|
||||||
from django.utils.encoding import smart_unicode
|
from django.utils.encoding import smart_unicode
|
||||||
from django.utils.xmlutils import SimplerXMLGenerator
|
from django.utils.xmlutils import SimplerXMLGenerator
|
||||||
from django.core.urlresolvers import resolve
|
from django.core.urlresolvers import resolve, reverse as django_reverse
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from djangorestframework.compat import StringIO
|
from djangorestframework.compat import StringIO
|
||||||
|
@ -173,3 +174,21 @@ class XMLRenderer():
|
||||||
|
|
||||||
def dict2xml(input):
|
def dict2xml(input):
|
||||||
return XMLRenderer().dict2xml(input)
|
return XMLRenderer().dict2xml(input)
|
||||||
|
|
||||||
|
|
||||||
|
def reverse(viewname, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Do the same as :py:func:`django.core.urlresolvers.reverse` but using
|
||||||
|
*request* to build a fully qualified URL.
|
||||||
|
"""
|
||||||
|
return request.build_absolute_uri(django_reverse(viewname, *args, **kwargs))
|
||||||
|
|
||||||
|
if django.VERSION >= (1, 4):
|
||||||
|
from django.core.urlresolvers import reverse_lazy as django_reverse_lazy
|
||||||
|
|
||||||
|
def reverse_lazy(viewname, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Do the same as :py:func:`django.core.urlresolvers.reverse_lazy` but using
|
||||||
|
*request* to build a fully qualified URL.
|
||||||
|
"""
|
||||||
|
return request.build_absolute_uri(django_reverse_lazy(viewname, *args, **kwargs))
|
||||||
|
|
|
@ -181,20 +181,12 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
|
||||||
Required if you want to do things like set `request.upload_handlers` before
|
Required if you want to do things like set `request.upload_handlers` before
|
||||||
the authentication and dispatch handling is run.
|
the authentication and dispatch handling is run.
|
||||||
"""
|
"""
|
||||||
# Calls to 'reverse' will not be fully qualified unless we set the
|
return request
|
||||||
# scheme/host/port here.
|
|
||||||
self.orig_prefix = get_script_prefix()
|
|
||||||
if not (self.orig_prefix.startswith('http:') or self.orig_prefix.startswith('https:')):
|
|
||||||
prefix = '%s://%s' % (request.is_secure() and 'https' or 'http', request.get_host())
|
|
||||||
set_script_prefix(prefix + self.orig_prefix)
|
|
||||||
|
|
||||||
def final(self, request, response, *args, **kargs):
|
def final(self, request, response, *args, **kargs):
|
||||||
"""
|
"""
|
||||||
Hook for any code that needs to run after everything else in the view.
|
Hook for any code that needs to run after everything else in the view.
|
||||||
"""
|
"""
|
||||||
# Restore script_prefix.
|
|
||||||
set_script_prefix(self.orig_prefix)
|
|
||||||
|
|
||||||
# Always add these headers.
|
# Always add these headers.
|
||||||
response.headers['Allow'] = ', '.join(self.allowed_methods)
|
response.headers['Allow'] = ', '.join(self.allowed_methods)
|
||||||
# sample to allow caching using Vary http header
|
# sample to allow caching using Vary http header
|
||||||
|
|
47
docs/howto/reverse.rst
Normal file
47
docs/howto/reverse.rst
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
Returning URIs from your Web APIs
|
||||||
|
=================================
|
||||||
|
|
||||||
|
"The central feature that distinguishes the REST architectural style from
|
||||||
|
other network-based styles is its emphasis on a uniform interface between
|
||||||
|
components."
|
||||||
|
|
||||||
|
-- Roy Fielding, Architectural Styles and the Design of Network-based Software Architectures
|
||||||
|
|
||||||
|
As a rule, it's probably better practice to return absolute URIs from you web APIs, e.g. "http://example.com/foobar", rather than returning relative URIs, e.g. "/foobar".
|
||||||
|
|
||||||
|
The advantages of doing so are:
|
||||||
|
|
||||||
|
* It's more explicit.
|
||||||
|
* It leaves less work for your API clients.
|
||||||
|
* There's no ambiguity about the meaning of the string when it's found in representations such as JSON that do not have a native URI type.
|
||||||
|
* It allows us to easily do things like markup HTML representations with hyperlinks.
|
||||||
|
|
||||||
|
Django REST framework provides two utility functions to make it simpler to return absolute URIs from your Web API.
|
||||||
|
|
||||||
|
There's no requirement for you to use them, but if you do then the self-describing API will be able to automatically hyperlink its output for you, which makes browsing the API much easier.
|
||||||
|
|
||||||
|
reverse(viewname, request, ...)
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
The :py:func:`~utils.reverse` function has the same behavior as :py:func:`django.core.urlresolvers.reverse` [1]_, except that it takes a request object and returns a fully qualified URL, using the request to determine the host and port::
|
||||||
|
|
||||||
|
from djangorestframework.utils import reverse
|
||||||
|
from djangorestframework.views import View
|
||||||
|
|
||||||
|
class MyView(View):
|
||||||
|
def get(self, request):
|
||||||
|
context = {
|
||||||
|
'url': reverse('year-summary', request, args=[1945])
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response(context)
|
||||||
|
|
||||||
|
reverse_lazy(viewname, request, ...)
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
The :py:func:`~utils.reverse_lazy` function has the same behavior as :py:func:`django.core.urlresolvers.reverse_lazy` [2]_, except that it takes a request object and returns a fully qualified URL, using the request to determine the host and port.
|
||||||
|
|
||||||
|
.. rubric:: Footnotes
|
||||||
|
|
||||||
|
.. [1] https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse
|
||||||
|
.. [2] https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-lazy
|
5
docs/library/utils.rst
Normal file
5
docs/library/utils.rst
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
:mod:`utils`
|
||||||
|
==============
|
||||||
|
|
||||||
|
.. automodule:: utils
|
||||||
|
:members:
|
|
@ -1,5 +1,5 @@
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from djangorestframework.resources import ModelResource
|
from djangorestframework.resources import ModelResource
|
||||||
|
from djangorestframework.utils import reverse
|
||||||
from blogpost.models import BlogPost, Comment
|
from blogpost.models import BlogPost, Comment
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ class BlogPostResource(ModelResource):
|
||||||
ordering = ('-created',)
|
ordering = ('-created',)
|
||||||
|
|
||||||
def comments(self, instance):
|
def comments(self, instance):
|
||||||
return reverse('comments', kwargs={'blogpost': instance.key})
|
return reverse('comments', request, kwargs={'blogpost': instance.key})
|
||||||
|
|
||||||
|
|
||||||
class CommentResource(ModelResource):
|
class CommentResource(ModelResource):
|
||||||
|
@ -24,4 +24,4 @@ class CommentResource(ModelResource):
|
||||||
ordering = ('-created',)
|
ordering = ('-created',)
|
||||||
|
|
||||||
def blogpost(self, instance):
|
def blogpost(self, instance):
|
||||||
return reverse('blog-post', kwargs={'key': instance.blogpost.key})
|
return reverse('blog-post', request, kwargs={'key': instance.blogpost.key})
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
"""Test a range of REST API usage of the example application.
|
"""Test a range of REST API usage of the example application.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.utils import simplejson as json
|
from django.utils import simplejson as json
|
||||||
|
|
||||||
from djangorestframework.compat import RequestFactory
|
from djangorestframework.compat import RequestFactory
|
||||||
|
from djangorestframework.utils import reverse
|
||||||
from djangorestframework.views import InstanceModelView, ListOrCreateModelView
|
from djangorestframework.views import InstanceModelView, ListOrCreateModelView
|
||||||
|
|
||||||
from blogpost import models, urls
|
from blogpost import models, urls
|
||||||
|
|
|
@ -2,9 +2,9 @@ from djangorestframework.compat import View # Use Django 1.3's django.views.gen
|
||||||
from djangorestframework.mixins import ResponseMixin
|
from djangorestframework.mixins import ResponseMixin
|
||||||
from djangorestframework.renderers import DEFAULT_RENDERERS
|
from djangorestframework.renderers import DEFAULT_RENDERERS
|
||||||
from djangorestframework.response import Response
|
from djangorestframework.response import Response
|
||||||
|
from djangorestframework.utils import reverse
|
||||||
|
|
||||||
from django.conf.urls.defaults import patterns, url
|
from django.conf.urls.defaults import patterns, url
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
|
|
||||||
|
|
||||||
class ExampleView(ResponseMixin, View):
|
class ExampleView(ResponseMixin, View):
|
||||||
|
@ -14,7 +14,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', request)})
|
||||||
return self.render(response)
|
return self.render(response)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
|
|
||||||
|
from djangorestframework.utils import reverse
|
||||||
from djangorestframework.views import View
|
from djangorestframework.views import View
|
||||||
from djangorestframework.response import Response
|
from djangorestframework.response import Response
|
||||||
from djangorestframework import status
|
from djangorestframework import status
|
||||||
|
@ -41,7 +41,7 @@ class ObjectStoreRoot(View):
|
||||||
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', kwargs={'key':key}) for key in ctime_sorted_basenames]
|
return [reverse('stored-object', request, kwargs={'key':key}) for key in ctime_sorted_basenames]
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""
|
"""
|
||||||
|
@ -51,7 +51,7 @@ class ObjectStoreRoot(View):
|
||||||
pathname = os.path.join(OBJECT_STORE_DIR, key)
|
pathname = os.path.join(OBJECT_STORE_DIR, key)
|
||||||
pickle.dump(self.CONTENT, open(pathname, 'wb'))
|
pickle.dump(self.CONTENT, open(pathname, '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', kwargs={'key':key})})
|
return Response(status.HTTP_201_CREATED, self.CONTENT, {'Location': reverse('stored-object', request, kwargs={'key':key})})
|
||||||
|
|
||||||
|
|
||||||
class StoredObject(View):
|
class StoredObject(View):
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from djangorestframework.views import View
|
from djangorestframework.views import View
|
||||||
from djangorestframework.permissions import PerUserThrottling, IsAuthenticated
|
from djangorestframework.permissions import PerUserThrottling, IsAuthenticated
|
||||||
from django.core.urlresolvers import reverse
|
from djangorestframework.utils import reverse
|
||||||
|
|
||||||
|
|
||||||
class PermissionsExampleView(View):
|
class PermissionsExampleView(View):
|
||||||
|
@ -12,11 +12,11 @@ class PermissionsExampleView(View):
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
'name': 'Throttling Example',
|
'name': 'Throttling Example',
|
||||||
'url': reverse('throttled-resource')
|
'url': reverse('throttled-resource', request)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Logged in example',
|
'name': 'Logged in example',
|
||||||
'url': reverse('loggedin-resource')
|
'url': reverse('loggedin-resource', request)
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from __future__ import with_statement # for python 2.5
|
from __future__ import with_statement # for python 2.5
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
|
|
||||||
from djangorestframework.resources import FormResource
|
from djangorestframework.resources import FormResource
|
||||||
from djangorestframework.response import Response
|
from djangorestframework.response import Response
|
||||||
from djangorestframework.renderers import BaseRenderer
|
from djangorestframework.renderers import BaseRenderer
|
||||||
|
from djangorestframework.utils import reverse
|
||||||
from djangorestframework.views import View
|
from djangorestframework.views import View
|
||||||
from djangorestframework import status
|
from djangorestframework import status
|
||||||
|
|
||||||
|
@ -61,7 +61,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', args=[unique_id]) for unique_id in unique_ids]
|
return [reverse('pygments-instance', request, args=[unique_id]) for unique_id in unique_ids]
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""
|
"""
|
||||||
|
@ -81,7 +81,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', args=[unique_id])})
|
return Response(status.HTTP_201_CREATED, headers={'Location': reverse('pygments-instance', request, args=[unique_id])})
|
||||||
|
|
||||||
|
|
||||||
class PygmentsInstance(View):
|
class PygmentsInstance(View):
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from django.core.urlresolvers import reverse
|
from djangorestframework.utils import reverse
|
||||||
|
|
||||||
from djangorestframework.views import View
|
from djangorestframework.views import View
|
||||||
from djangorestframework.response import Response
|
from djangorestframework.response import Response
|
||||||
from djangorestframework import status
|
from djangorestframework import status
|
||||||
|
@ -16,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', kwargs={'num':num}) for num in range(3)]}
|
return {"Some other resources": [reverse('another-example', request, kwargs={'num':num}) for num in range(3)]}
|
||||||
|
|
||||||
|
|
||||||
class AnotherExampleView(View):
|
class AnotherExampleView(View):
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""The root view for the examples provided with Django REST framework"""
|
"""The root view for the examples provided with Django REST framework"""
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from djangorestframework.utils import reverse
|
||||||
from djangorestframework.views import View
|
from djangorestframework.views import 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')},
|
return [{'name': 'Simple Resource example', 'url': reverse('example-resource', request)},
|
||||||
{'name': 'Simple ModelResource example', 'url': reverse('model-resource-root')},
|
{'name': 'Simple ModelResource example', 'url': reverse('model-resource-root', request)},
|
||||||
{'name': 'Simple Mixin-only example', 'url': reverse('mixin-view')},
|
{'name': 'Simple Mixin-only example', 'url': reverse('mixin-view', request)},
|
||||||
{'name': 'Object store API', 'url': reverse('object-store-root')},
|
{'name': 'Object store API', 'url': reverse('object-store-root', request)},
|
||||||
{'name': 'Code highlighting API', 'url': reverse('pygments-root')},
|
{'name': 'Code highlighting API', 'url': reverse('pygments-root', request)},
|
||||||
{'name': 'Blog posts API', 'url': reverse('blog-posts-root')},
|
{'name': 'Blog posts API', 'url': reverse('blog-posts-root', request)},
|
||||||
{'name': 'Permissions example', 'url': reverse('permissions-example')}
|
{'name': 'Permissions example', 'url': reverse('permissions-example', request)}
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user