mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-23 01:57:00 +03:00
Merge branch 'master' of https://github.com/tomchristie/django-rest-framework
This commit is contained in:
commit
4cd49d5de3
|
@ -127,7 +127,7 @@ This pagination style mirrors the syntax used when looking up multiple database
|
||||||
|
|
||||||
#### Setup
|
#### Setup
|
||||||
|
|
||||||
To enable the `PageNumberPagination` style globally, use the following configuration:
|
To enable the `LimitOffsetPagination` style globally, use the following configuration:
|
||||||
|
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination'
|
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination'
|
||||||
|
|
|
@ -65,7 +65,7 @@ When testing views directly using a request factory, it's often convenient to be
|
||||||
|
|
||||||
To forcibly authenticate a request, use the `force_authenticate()` method.
|
To forcibly authenticate a request, use the `force_authenticate()` method.
|
||||||
|
|
||||||
from rest_framework.tests import force_authenticate
|
from rest_framework.test import force_authenticate
|
||||||
|
|
||||||
factory = APIRequestFactory()
|
factory = APIRequestFactory()
|
||||||
user = User.objects.get(username='olivia')
|
user = User.objects.get(username='olivia')
|
||||||
|
|
|
@ -206,4 +206,4 @@ This will either be made as a single 3.2 release, or split across two separate r
|
||||||
[pagination]: ../api-guide/pagination.md
|
[pagination]: ../api-guide/pagination.md
|
||||||
[versioning]: ../api-guide/versioning.md
|
[versioning]: ../api-guide/versioning.md
|
||||||
[internationalization]: internationalization.md
|
[internationalization]: internationalization.md
|
||||||
[customizing-field-mappings]: ../api-guide/serializers.md/#customizing-field-mappings
|
[customizing-field-mappings]: ../api-guide/serializers.md#customizing-field-mappings
|
||||||
|
|
|
@ -154,7 +154,7 @@ For older release notes, [please see the version 2.x documentation](old-release-
|
||||||
[2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion
|
[2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion
|
||||||
[ticket-582]: https://github.com/tomchristie/django-rest-framework/issues/582
|
[ticket-582]: https://github.com/tomchristie/django-rest-framework/issues/582
|
||||||
[rfc-6266]: http://tools.ietf.org/html/rfc6266#section-4.3
|
[rfc-6266]: http://tools.ietf.org/html/rfc6266#section-4.3
|
||||||
[old-release-notes]: http://tomchristie.github.io/rest-framework-2-docs/topics/release-notes#24x-series
|
[old-release-notes]: https://github.com/tomchristie/django-rest-framework/blob/version-2.4.x/docs/topics/release-notes.md
|
||||||
|
|
||||||
[3.0.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.1+Release%22
|
[3.0.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.1+Release%22
|
||||||
[3.0.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.2+Release%22
|
[3.0.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.2+Release%22
|
||||||
|
|
|
@ -200,7 +200,7 @@ See the [browsable api][browsable-api] topic for more information about the brow
|
||||||
|
|
||||||
In [tutorial part 3][tut-3], we'll start using class based views, and see how generic views reduce the amount of code we need to write.
|
In [tutorial part 3][tut-3], we'll start using class based views, and see how generic views reduce the amount of code we need to write.
|
||||||
|
|
||||||
[json-url]: http://example.com/api/items/4.json
|
[json-url]: http://example.com/api/items/4/.json
|
||||||
[devserver]: http://127.0.0.1:8000/snippets/
|
[devserver]: http://127.0.0.1:8000/snippets/
|
||||||
[browsable-api]: ../topics/browsable-api.md
|
[browsable-api]: ../topics/browsable-api.md
|
||||||
[tut-1]: 1-serialization.md
|
[tut-1]: 1-serialization.md
|
||||||
|
|
|
@ -10,7 +10,7 @@ from django.core.paginator import InvalidPage, Paginator as DjangoPaginator
|
||||||
from django.template import Context, loader
|
from django.template import Context, loader
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.six.moves.urllib import parse as urlparse
|
from django.utils.six.moves.urllib import parse as urlparse
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework.compat import OrderedDict
|
from rest_framework.compat import OrderedDict
|
||||||
from rest_framework.exceptions import NotFound
|
from rest_framework.exceptions import NotFound
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
|
@ -218,14 +218,15 @@ class SimpleRouter(BaseRouter):
|
||||||
|
|
||||||
https://github.com/alanjds/drf-nested-routers
|
https://github.com/alanjds/drf-nested-routers
|
||||||
"""
|
"""
|
||||||
base_regex = '(?P<{lookup_prefix}{lookup_field}>{lookup_value})'
|
base_regex = '(?P<{lookup_prefix}{lookup_url_kwarg}>{lookup_value})'
|
||||||
# Use `pk` as default field, unset set. Default regex should not
|
# Use `pk` as default field, unset set. Default regex should not
|
||||||
# consume `.json` style suffixes and should break at '/' boundaries.
|
# consume `.json` style suffixes and should break at '/' boundaries.
|
||||||
lookup_field = getattr(viewset, 'lookup_field', 'pk')
|
lookup_field = getattr(viewset, 'lookup_field', 'pk')
|
||||||
|
lookup_url_kwarg = getattr(viewset, 'lookup_url_kwarg', None) or lookup_field
|
||||||
lookup_value = getattr(viewset, 'lookup_value_regex', '[^/.]+')
|
lookup_value = getattr(viewset, 'lookup_value_regex', '[^/.]+')
|
||||||
return base_regex.format(
|
return base_regex.format(
|
||||||
lookup_prefix=lookup_prefix,
|
lookup_prefix=lookup_prefix,
|
||||||
lookup_field=lookup_field,
|
lookup_url_kwarg=lookup_url_kwarg,
|
||||||
lookup_value=lookup_value
|
lookup_value=lookup_value
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,6 @@
|
||||||
{% if next_url %}
|
{% if next_url %}
|
||||||
<li class="next"><a href="{{ next_url }}">Next »</a></li>
|
<li class="next"><a href="{{ next_url }}">Next »</a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li class="next disabled"><a href="#">Next »</li>
|
<li class="next disabled"><a href="#">Next »</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -18,7 +18,6 @@ def pytest_configure():
|
||||||
MIDDLEWARE_CLASSES=(
|
MIDDLEWARE_CLASSES=(
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
),
|
),
|
||||||
|
@ -27,7 +26,6 @@ def pytest_configure():
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.sites',
|
'django.contrib.sites',
|
||||||
'django.contrib.messages',
|
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
|
@ -35,12 +33,7 @@ def pytest_configure():
|
||||||
'tests',
|
'tests',
|
||||||
),
|
),
|
||||||
PASSWORD_HASHERS=(
|
PASSWORD_HASHERS=(
|
||||||
'django.contrib.auth.hashers.SHA1PasswordHasher',
|
|
||||||
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
|
|
||||||
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
|
|
||||||
'django.contrib.auth.hashers.BCryptPasswordHasher',
|
|
||||||
'django.contrib.auth.hashers.MD5PasswordHasher',
|
'django.contrib.auth.hashers.MD5PasswordHasher',
|
||||||
'django.contrib.auth.hashers.CryptPasswordHasher',
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,13 @@ class NoteViewSet(viewsets.ModelViewSet):
|
||||||
lookup_field = 'uuid'
|
lookup_field = 'uuid'
|
||||||
|
|
||||||
|
|
||||||
|
class KWargedNoteViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = RouterTestModel.objects.all()
|
||||||
|
serializer_class = NoteSerializer
|
||||||
|
lookup_field = 'text__contains'
|
||||||
|
lookup_url_kwarg = 'text'
|
||||||
|
|
||||||
|
|
||||||
class MockViewSet(viewsets.ModelViewSet):
|
class MockViewSet(viewsets.ModelViewSet):
|
||||||
queryset = None
|
queryset = None
|
||||||
serializer_class = None
|
serializer_class = None
|
||||||
|
@ -40,6 +47,9 @@ class MockViewSet(viewsets.ModelViewSet):
|
||||||
notes_router = SimpleRouter()
|
notes_router = SimpleRouter()
|
||||||
notes_router.register(r'notes', NoteViewSet)
|
notes_router.register(r'notes', NoteViewSet)
|
||||||
|
|
||||||
|
kwarged_notes_router = SimpleRouter()
|
||||||
|
kwarged_notes_router.register(r'notes', KWargedNoteViewSet)
|
||||||
|
|
||||||
namespaced_router = DefaultRouter()
|
namespaced_router = DefaultRouter()
|
||||||
namespaced_router.register(r'example', MockViewSet, base_name='example')
|
namespaced_router.register(r'example', MockViewSet, base_name='example')
|
||||||
|
|
||||||
|
@ -47,6 +57,7 @@ urlpatterns = [
|
||||||
url(r'^non-namespaced/', include(namespaced_router.urls)),
|
url(r'^non-namespaced/', include(namespaced_router.urls)),
|
||||||
url(r'^namespaced/', include(namespaced_router.urls, namespace='example')),
|
url(r'^namespaced/', include(namespaced_router.urls, namespace='example')),
|
||||||
url(r'^example/', include(notes_router.urls)),
|
url(r'^example/', include(notes_router.urls)),
|
||||||
|
url(r'^example2/', include(kwarged_notes_router.urls)),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -177,6 +188,33 @@ class TestLookupValueRegex(TestCase):
|
||||||
self.assertEqual(expected[idx], self.urls[idx].regex.pattern)
|
self.assertEqual(expected[idx], self.urls[idx].regex.pattern)
|
||||||
|
|
||||||
|
|
||||||
|
class TestLookupUrlKwargs(TestCase):
|
||||||
|
"""
|
||||||
|
Ensure the router honors lookup_url_kwarg.
|
||||||
|
|
||||||
|
Setup a deep lookup_field, but map it to a simple URL kwarg.
|
||||||
|
"""
|
||||||
|
urls = 'tests.test_routers'
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
RouterTestModel.objects.create(uuid='123', text='foo bar')
|
||||||
|
|
||||||
|
def test_custom_lookup_url_kwarg_route(self):
|
||||||
|
detail_route = kwarged_notes_router.urls[-1]
|
||||||
|
detail_url_pattern = detail_route.regex.pattern
|
||||||
|
self.assertIn('^notes/(?P<text>', detail_url_pattern)
|
||||||
|
|
||||||
|
def test_retrieve_lookup_url_kwarg_detail_view(self):
|
||||||
|
response = self.client.get('/example2/notes/fo/')
|
||||||
|
self.assertEqual(
|
||||||
|
response.data,
|
||||||
|
{
|
||||||
|
"url": "http://testserver/example/notes/123/",
|
||||||
|
"uuid": "123", "text": "foo bar"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestTrailingSlashIncluded(TestCase):
|
class TestTrailingSlashIncluded(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
class NoteViewSet(viewsets.ModelViewSet):
|
class NoteViewSet(viewsets.ModelViewSet):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user