aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Christie2015-03-16 11:57:42 +0000
committerTom Christie2015-03-16 11:57:42 +0000
commit4cd49d5de38b860e4b2260d7fa82dbdf9256c6e8 (patch)
tree139e2ae4df198e3d672e7b47ef875e48cf1311da
parentc573e7b4a8b4fedfe70b934571284d84e9a81103 (diff)
parent1c0db6dda8235bc0bfab2370ea6c6d0fd08a1da5 (diff)
downloaddjango-rest-framework-4cd49d5de38b860e4b2260d7fa82dbdf9256c6e8.tar.bz2
Merge branch 'master' of https://github.com/tomchristie/django-rest-framework
-rw-r--r--docs/api-guide/pagination.md2
-rw-r--r--docs/api-guide/testing.md2
-rw-r--r--docs/topics/3.1-announcement.md2
-rw-r--r--docs/topics/release-notes.md2
-rw-r--r--docs/tutorial/2-requests-and-responses.md2
-rw-r--r--rest_framework/pagination.py2
-rw-r--r--rest_framework/routers.py5
-rw-r--r--rest_framework/templates/rest_framework/pagination/previous_and_next.html2
-rw-r--r--tests/conftest.py7
-rw-r--r--tests/test_routers.py38
10 files changed, 48 insertions, 16 deletions
diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md
index 3518fb34..eca468b8 100644
--- a/docs/api-guide/pagination.md
+++ b/docs/api-guide/pagination.md
@@ -127,7 +127,7 @@ This pagination style mirrors the syntax used when looking up multiple database
#### Setup
-To enable the `PageNumberPagination` style globally, use the following configuration:
+To enable the `LimitOffsetPagination` style globally, use the following configuration:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination'
diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md
index 8a848c20..dd3295c4 100644
--- a/docs/api-guide/testing.md
+++ b/docs/api-guide/testing.md
@@ -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.
- from rest_framework.tests import force_authenticate
+ from rest_framework.test import force_authenticate
factory = APIRequestFactory()
user = User.objects.get(username='olivia')
diff --git a/docs/topics/3.1-announcement.md b/docs/topics/3.1-announcement.md
index 6eb3681f..80d4007e 100644
--- a/docs/topics/3.1-announcement.md
+++ b/docs/topics/3.1-announcement.md
@@ -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
[versioning]: ../api-guide/versioning.md
[internationalization]: internationalization.md
-[customizing-field-mappings]: ../api-guide/serializers.md/#customizing-field-mappings
+[customizing-field-mappings]: ../api-guide/serializers.md#customizing-field-mappings
diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md
index 35592feb..84d310c2 100644
--- a/docs/topics/release-notes.md
+++ b/docs/topics/release-notes.md
@@ -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
[ticket-582]: https://github.com/tomchristie/django-rest-framework/issues/582
[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.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.2+Release%22
diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md
index e2c173d6..2cad1e5d 100644
--- a/docs/tutorial/2-requests-and-responses.md
+++ b/docs/tutorial/2-requests-and-responses.md
@@ -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.
-[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/
[browsable-api]: ../topics/browsable-api.md
[tut-1]: 1-serialization.md
diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py
index f41a9ae1..5e60448d 100644
--- a/rest_framework/pagination.py
+++ b/rest_framework/pagination.py
@@ -10,7 +10,7 @@ from django.core.paginator import InvalidPage, Paginator as DjangoPaginator
from django.template import Context, loader
from django.utils import six
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.exceptions import NotFound
from rest_framework.response import Response
diff --git a/rest_framework/routers.py b/rest_framework/routers.py
index b1e39ff7..4df852bf 100644
--- a/rest_framework/routers.py
+++ b/rest_framework/routers.py
@@ -218,14 +218,15 @@ class SimpleRouter(BaseRouter):
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
# consume `.json` style suffixes and should break at '/' boundaries.
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', '[^/.]+')
return base_regex.format(
lookup_prefix=lookup_prefix,
- lookup_field=lookup_field,
+ lookup_url_kwarg=lookup_url_kwarg,
lookup_value=lookup_value
)
diff --git a/rest_framework/templates/rest_framework/pagination/previous_and_next.html b/rest_framework/templates/rest_framework/pagination/previous_and_next.html
index eacbfff4..08c17709 100644
--- a/rest_framework/templates/rest_framework/pagination/previous_and_next.html
+++ b/rest_framework/templates/rest_framework/pagination/previous_and_next.html
@@ -7,6 +7,6 @@
{% if next_url %}
<li class="next"><a href="{{ next_url }}">Next &raquo;</a></li>
{% else %}
- <li class="next disabled"><a href="#">Next &raquo;</li>
+ <li class="next disabled"><a href="#">Next &raquo;</a></li>
{% endif %}
</ul>
diff --git a/tests/conftest.py b/tests/conftest.py
index 44ed070b..03b4e328 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -18,7 +18,6 @@ def pytest_configure():
MIDDLEWARE_CLASSES=(
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
),
@@ -27,7 +26,6 @@ def pytest_configure():
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
- 'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
@@ -35,12 +33,7 @@ def pytest_configure():
'tests',
),
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.CryptPasswordHasher',
),
)
diff --git a/tests/test_routers.py b/tests/test_routers.py
index 08c58ec7..19eeb868 100644
--- a/tests/test_routers.py
+++ b/tests/test_routers.py
@@ -32,6 +32,13 @@ class NoteViewSet(viewsets.ModelViewSet):
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):
queryset = None
serializer_class = None
@@ -40,6 +47,9 @@ class MockViewSet(viewsets.ModelViewSet):
notes_router = SimpleRouter()
notes_router.register(r'notes', NoteViewSet)
+kwarged_notes_router = SimpleRouter()
+kwarged_notes_router.register(r'notes', KWargedNoteViewSet)
+
namespaced_router = DefaultRouter()
namespaced_router.register(r'example', MockViewSet, base_name='example')
@@ -47,6 +57,7 @@ urlpatterns = [
url(r'^non-namespaced/', include(namespaced_router.urls)),
url(r'^namespaced/', include(namespaced_router.urls, namespace='example')),
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)
+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):
def setUp(self):
class NoteViewSet(viewsets.ModelViewSet):