From da385c9c1f9deeeefd705154a6e6612d6d62f41b Mon Sep 17 00:00:00 2001 From: Collin Anderson Date: Tue, 23 Sep 2014 17:08:38 -0400 Subject: [PATCH 1/3] remove patterns and strings from urls #1898 --- docs/api-guide/authentication.md | 7 ++++--- docs/api-guide/format-suffixes.md | 13 +++++++------ docs/index.md | 4 ++-- docs/topics/2.3-announcement.md | 6 +++--- docs/tutorial/1-serialization.md | 13 +++++++------ docs/tutorial/2-requests-and-responses.md | 9 +++++---- docs/tutorial/3-class-based-views.md | 4 ++-- docs/tutorial/4-authentication-and-permissions.md | 4 ++-- .../5-relationships-and-hyperlinked-apis.md | 10 +++++----- docs/tutorial/6-viewsets-and-routers.md | 12 ++++++------ 10 files changed, 43 insertions(+), 39 deletions(-) diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 343466eee..0ec5bad10 100755 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -190,9 +190,10 @@ If you've already created some users, you can generate tokens for all existing u When using `TokenAuthentication`, you may want to provide a mechanism for clients to obtain a token given the username and password. REST framework provides a built-in view to provide this behavior. To use it, add the `obtain_auth_token` view to your URLconf: - urlpatterns += patterns('', - url(r'^api-token-auth/', 'rest_framework.authtoken.views.obtain_auth_token') - ) + from rest_framework.authtoken import views + urlpatterns += [ + url(r'^api-token-auth/', views.obtain_auth_token) + ] Note that the URL part of the pattern can be whatever you want to use. diff --git a/docs/api-guide/format-suffixes.md b/docs/api-guide/format-suffixes.md index 529738e3b..76a3367b0 100644 --- a/docs/api-guide/format-suffixes.md +++ b/docs/api-guide/format-suffixes.md @@ -26,12 +26,13 @@ Arguments: Example: from rest_framework.urlpatterns import format_suffix_patterns - - urlpatterns = patterns('blog.views', - url(r'^/$', 'api_root'), - url(r'^comments/$', 'comment_list'), - url(r'^comments/(?P[0-9]+)/$', 'comment_detail') - ) + from blog import views + + urlpatterns = [ + url(r'^/$', views.apt_root), + url(r'^comments/$', views.comment_list), + url(r'^comments/(?P[0-9]+)/$', views.comment_detail) + ] urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html']) diff --git a/docs/index.md b/docs/index.md index 6dcb962f5..e4c971f95 100644 --- a/docs/index.md +++ b/docs/index.md @@ -85,10 +85,10 @@ Add `'rest_framework'` to your `INSTALLED_APPS` setting. If you're intending to use the browsable API you'll probably also want to add REST framework's login and logout views. Add the following to your root `urls.py` file. - urlpatterns = patterns('', + urlpatterns = [ ... url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) - ) + ] Note that the URL path can be whatever you want, but you must include `'rest_framework.urls'` with the `'rest_framework'` namespace. diff --git a/docs/topics/2.3-announcement.md b/docs/topics/2.3-announcement.md index ba4351459..7c800afa0 100644 --- a/docs/topics/2.3-announcement.md +++ b/docs/topics/2.3-announcement.md @@ -15,7 +15,7 @@ As an example of just how simple REST framework APIs can now be, here's an API w """ A REST framework API for viewing and editing users and groups. """ - from django.conf.urls.defaults import url, patterns, include + from django.conf.urls.defaults import url, include from django.contrib.auth.models import User, Group from rest_framework import viewsets, routers @@ -36,10 +36,10 @@ As an example of just how simple REST framework APIs can now be, here's an API w # Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browseable API. - urlpatterns = patterns('', + urlpatterns = [ url(r'^', include(router.urls)), url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) - ) + ] The best place to get started with ViewSets and Routers is to take a look at the [newest section in the tutorial][part-6], which demonstrates their usage. diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index 96214f5b4..b0565d91a 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -64,9 +64,9 @@ We'll also need to add our new `snippets` app and the `rest_framework` app to `I We also need to wire up the root urlconf, in the `tutorial/urls.py` file, to include our snippet app's URLs. - urlpatterns = patterns('', + urlpatterns = [ url(r'^', include('snippets.urls')), - ) + ] Okay, we're ready to roll. @@ -297,11 +297,12 @@ We'll also need a view which corresponds to an individual snippet, and can be us Finally we need to wire these views up. Create the `snippets/urls.py` file: from django.conf.urls import patterns, url + from snippets import views - urlpatterns = patterns('snippets.views', - url(r'^snippets/$', 'snippet_list'), - url(r'^snippets/(?P[0-9]+)/$', 'snippet_detail'), - ) + urlpatterns = [ + url(r'^snippets/$', views.snippet_list), + url(r'^snippets/(?P[0-9]+)/$', views.snippet_detail), + ] It's worth noting that there are a couple of edge cases we're not dealing with properly at the moment. If we send malformed `json`, or if a request is made with a method that the view doesn't handle, then we'll end up with a 500 "server error" response. Still, this'll do for now. diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index e70bbbfc4..136b01351 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -110,11 +110,12 @@ Now update the `urls.py` file slightly, to append a set of `format_suffix_patter from django.conf.urls import patterns, url from rest_framework.urlpatterns import format_suffix_patterns + from snippets import views - urlpatterns = patterns('snippets.views', - url(r'^snippets/$', 'snippet_list'), - url(r'^snippets/(?P[0-9]+)$', 'snippet_detail'), - ) + urlpatterns = [ + url(r'^snippets/$', views.snippet_list), + url(r'^snippets/(?P[0-9]+)$', views.snippet_detail), + ] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/docs/tutorial/3-class-based-views.md b/docs/tutorial/3-class-based-views.md index e04072ca5..382f078a3 100644 --- a/docs/tutorial/3-class-based-views.md +++ b/docs/tutorial/3-class-based-views.md @@ -68,10 +68,10 @@ We'll also need to refactor our `urls.py` slightly now we're using class based v from rest_framework.urlpatterns import format_suffix_patterns from snippets import views - urlpatterns = patterns('', + urlpatterns = [ url(r'^snippets/$', views.SnippetList.as_view()), url(r'^snippets/(?P[0-9]+)/$', views.SnippetDetail.as_view()), - ) + ] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/docs/tutorial/4-authentication-and-permissions.md b/docs/tutorial/4-authentication-and-permissions.md index 74ad9a551..9120e2547 100644 --- a/docs/tutorial/4-authentication-and-permissions.md +++ b/docs/tutorial/4-authentication-and-permissions.md @@ -137,10 +137,10 @@ Add the following import at the top of the file: And, at the end of the file, add a pattern to include the login and logout views for the browsable API. - urlpatterns += patterns('', + urlpatterns += [ url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), - ) + ] The `r'^api-auth/'` part of pattern can actually be whatever URL you want to use. The only restriction is that the included urls must use the `'rest_framework'` namespace. diff --git a/docs/tutorial/5-relationships-and-hyperlinked-apis.md b/docs/tutorial/5-relationships-and-hyperlinked-apis.md index 9c61fe3d3..36473ce91 100644 --- a/docs/tutorial/5-relationships-and-hyperlinked-apis.md +++ b/docs/tutorial/5-relationships-and-hyperlinked-apis.md @@ -108,8 +108,8 @@ If we're going to have a hyperlinked API, we need to make sure we name our URL p After adding all those names into our URLconf, our final `snippets/urls.py` file should look something like this: # API endpoints - urlpatterns = format_suffix_patterns(patterns('snippets.views', - url(r'^$', 'api_root'), + urlpatterns = format_suffix_patterns([ + url(r'^$', views.api_root), url(r'^snippets/$', views.SnippetList.as_view(), name='snippet-list'), @@ -125,13 +125,13 @@ After adding all those names into our URLconf, our final `snippets/urls.py` file url(r'^users/(?P[0-9]+)/$', views.UserDetail.as_view(), name='user-detail') - )) + ]) # Login and logout views for the browsable API - urlpatterns += patterns('', + urlpatterns += [ url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), - ) + ] ## Adding pagination diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md index b20195204..cf37a2601 100644 --- a/docs/tutorial/6-viewsets-and-routers.md +++ b/docs/tutorial/6-viewsets-and-routers.md @@ -87,14 +87,14 @@ Notice how we're creating multiple views from each `ViewSet` class, by binding t Now that we've bound our resources into concrete views, we can register the views with the URL conf as usual. - urlpatterns = format_suffix_patterns(patterns('snippets.views', - url(r'^$', 'api_root'), + urlpatterns = format_suffix_patterns([ + url(r'^$', api_root), url(r'^snippets/$', snippet_list, name='snippet-list'), url(r'^snippets/(?P[0-9]+)/$', snippet_detail, name='snippet-detail'), url(r'^snippets/(?P[0-9]+)/highlight/$', snippet_highlight, name='snippet-highlight'), url(r'^users/$', user_list, name='user-list'), url(r'^users/(?P[0-9]+)/$', user_detail, name='user-detail') - )) + ]) ## Using Routers @@ -102,7 +102,7 @@ Because we're using `ViewSet` classes rather than `View` classes, we actually do Here's our re-wired `urls.py` file. - from django.conf.urls import patterns, url, include + from django.conf.urls import url, include from snippets import views from rest_framework.routers import DefaultRouter @@ -113,10 +113,10 @@ Here's our re-wired `urls.py` file. # The API URLs are now determined automatically by the router. # Additionally, we include the login URLs for the browseable API. - urlpatterns = patterns('', + urlpatterns = [ url(r'^', include(router.urls)), url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) - ) + ] Registering the viewsets with the router is similar to providing a urlpattern. We include two arguments - the URL prefix for the views, and the viewset itself. From e8c01ecdabe2abda48dd0cf298d4b6c743574449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Padilla?= Date: Tue, 23 Sep 2014 21:12:58 -0400 Subject: [PATCH 2/3] Correctly propagate cloned_request for OPTIONS Update to fix pending changes in #1507 --- rest_framework/generics.py | 7 +++++-- tests/test_generics.py | 39 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/rest_framework/generics.py b/rest_framework/generics.py index a6f686571..a62da00b3 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -398,10 +398,11 @@ class GenericAPIView(views.APIView): if method not in self.allowed_methods: continue - cloned_request = clone_request(request, method) + original_request = self.request + self.request = clone_request(request, method) try: # Test global permissions - self.check_permissions(cloned_request) + self.check_permissions(self.request) # Test object permissions if method == 'PUT': try: @@ -419,6 +420,8 @@ class GenericAPIView(views.APIView): # appropriate metadata about the fields that should be supplied. serializer = self.get_serializer() actions[method] = serializer.metadata() + finally: + self.request = original_request if actions: ret['actions'] = actions diff --git a/tests/test_generics.py b/tests/test_generics.py index e9f5bebdd..dd3bab186 100644 --- a/tests/test_generics.py +++ b/tests/test_generics.py @@ -681,3 +681,42 @@ class TestFilterBackendAppliedToViews(TestCase): response = view(request).render() self.assertContains(response, 'field_b') self.assertNotContains(response, 'field_a') + + def test_options_with_dynamic_serializer(self): + """ + Ensure that OPTIONS returns correct POST json schema: + DynamicSerializer with single field 'field_b' + """ + request = factory.options('/') + view = DynamicSerializerView.as_view() + + with self.assertNumQueries(0): + response = view(request).render() + + expected = { + 'name': 'Dynamic Serializer', + 'description': '', + 'renders': [ + 'text/html', + 'application/json' + ], + 'parses': [ + 'application/json', + 'application/x-www-form-urlencoded', + 'multipart/form-data' + ], + 'actions': { + 'POST': { + 'field_b': { + 'type': u'string', + 'required': True, + 'read_only': False, + 'label': u'field b', + 'max_length': 100 + } + } + } + } + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, expected) From 90139b3efc7da7a2c396882b6905291269387ace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Padilla?= Date: Tue, 23 Sep 2014 21:18:56 -0400 Subject: [PATCH 3/3] Remove left unicode strings --- tests/test_generics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_generics.py b/tests/test_generics.py index dd3bab186..97116349b 100644 --- a/tests/test_generics.py +++ b/tests/test_generics.py @@ -708,10 +708,10 @@ class TestFilterBackendAppliedToViews(TestCase): 'actions': { 'POST': { 'field_b': { - 'type': u'string', + 'type': 'string', 'required': True, 'read_only': False, - 'label': u'field b', + 'label': 'field b', 'max_length': 100 } }