From af8a362d6b513b71de45109b441f79ed7d1b103c Mon Sep 17 00:00:00 2001 From: Nicolas Delaby Date: Mon, 7 Apr 2014 14:59:27 +0200 Subject: reset stored credentials when call client.logout() --- rest_framework/test.py | 4 ++++ rest_framework/tests/test_testing.py | 11 +++++++++++ 2 files changed, 15 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/test.py b/rest_framework/test.py index df5a5b3b..79982cb0 100644 --- a/rest_framework/test.py +++ b/rest_framework/test.py @@ -154,6 +154,10 @@ class APIClient(APIRequestFactory, DjangoClient): kwargs.update(self._credentials) return super(APIClient, self).request(**kwargs) + def logout(self): + self._credentials = {} + return super(APIClient, self).logout() + class APITransactionTestCase(testcases.TransactionTestCase): client_class = APIClient diff --git a/rest_framework/tests/test_testing.py b/rest_framework/tests/test_testing.py index a55d4b22..b16d1962 100644 --- a/rest_framework/tests/test_testing.py +++ b/rest_framework/tests/test_testing.py @@ -99,6 +99,17 @@ class TestAPITestClient(TestCase): self.assertEqual(response.status_code, 403) self.assertEqual(response.data, expected) + def test_can_logout(self): + """ + `logout()` reset stored credentials + """ + self.client.credentials(HTTP_AUTHORIZATION='example') + response = self.client.get('/view/') + self.assertEqual(response.data['auth'], 'example') + self.client.logout() + response = self.client.get('/view/') + self.assertEqual(response.data['auth'], b'') + class TestAPIRequestFactory(TestCase): def test_csrf_exempt_by_default(self): -- cgit v1.2.3 From b4c7717cb80cb13a2f13aae8855e226685306880 Mon Sep 17 00:00:00 2001 From: Walt Javins Date: Fri, 13 Jun 2014 22:26:00 -0700 Subject: Refactor login template to extend base. While experimenting with extending DRF, I found that the login page 1) had no title, and 2) duplicated info from base.html. This change adds a new {% block body %} to the base.html template which allows override of the entire html . login_base.html has its duplicated head info stripped, and now extends base.html to share common html templating. As part of this change, pretify.css is unnecessarily added to login_base.html. If this is deemed a problem, it will be easy to block that css out, and have login_base.html override the block. Ideally, I would have liked to create a new api_base.html that extends base.html, move the api specific logic into that template, and leave base.html content agnostic, to truely be a unifying base for all DRF pages. But this change would break current apps that override api.html and expect base.html to be the immediate super template. :/ This change is benificial because it: - removes duplication of header declarations (mostly css includes) - adds a html title to the login page - standardizes html header info across all DRF pages Docs are updated to reflect the new structure. --- rest_framework/templates/rest_framework/base.html | 2 ++ rest_framework/templates/rest_framework/login_base.html | 15 +++------------ 2 files changed, 5 insertions(+), 12 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index 7067ee2f..1f3def8f 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -24,6 +24,7 @@ {% endblock %} + {% block body %}
@@ -230,4 +231,5 @@ {% endblock %} + {% endblock %} diff --git a/rest_framework/templates/rest_framework/login_base.html b/rest_framework/templates/rest_framework/login_base.html index be9a0072..312a1138 100644 --- a/rest_framework/templates/rest_framework/login_base.html +++ b/rest_framework/templates/rest_framework/login_base.html @@ -1,17 +1,8 @@ +{% extends "rest_framework/base.html" %} {% load url from future %} {% load rest_framework %} - - - - {% block style %} - {% block bootstrap_theme %} - - - {% endblock %} - - {% endblock %} - + {% block body %}
@@ -50,4 +41,4 @@
- + {% endblock %} -- cgit v1.2.3 From 05882cc5999088ac232788ae62717c061e74ad12 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Fri, 25 Jul 2014 10:55:53 +0000 Subject: Sending "Bearer" and "Bearer " resulted in a 500. --- rest_framework/authentication.py | 14 +++++++------- rest_framework/tests/test_authentication.py | 9 +++++++++ 2 files changed, 16 insertions(+), 7 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index da9ca510..887ef5d7 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -310,6 +310,13 @@ class OAuth2Authentication(BaseAuthentication): auth = get_authorization_header(request).split() + if len(auth) == 1: + msg = 'Invalid bearer header. No credentials provided.' + raise exceptions.AuthenticationFailed(msg) + elif len(auth) > 2: + msg = 'Invalid bearer header. Token string should not contain spaces.' + raise exceptions.AuthenticationFailed(msg) + if auth and auth[0].lower() == b'bearer': access_token = auth[1] elif 'access_token' in request.POST: @@ -319,13 +326,6 @@ class OAuth2Authentication(BaseAuthentication): else: return None - if len(auth) == 1: - msg = 'Invalid bearer header. No credentials provided.' - raise exceptions.AuthenticationFailed(msg) - elif len(auth) > 2: - msg = 'Invalid bearer header. Token string should not contain spaces.' - raise exceptions.AuthenticationFailed(msg) - return self.authenticate_credentials(request, access_token) def authenticate_credentials(self, request, access_token): diff --git a/rest_framework/tests/test_authentication.py b/rest_framework/tests/test_authentication.py index a1c43d9c..cf8415ed 100644 --- a/rest_framework/tests/test_authentication.py +++ b/rest_framework/tests/test_authentication.py @@ -549,6 +549,15 @@ class OAuth2Tests(TestCase): response = self.csrf_client.get('/oauth2-test/', HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 401) + @unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed') + def test_get_form_with_wrong_authorization_header_token_missing(self): + """Ensure that a wrong token lead to the correct HTTP error status code""" + auth = "Bearer" + response = self.csrf_client.get('/oauth2-test/', {}, HTTP_AUTHORIZATION=auth) + self.assertEqual(response.status_code, 401) + response = self.csrf_client.get('/oauth2-test/', HTTP_AUTHORIZATION=auth) + self.assertEqual(response.status_code, 401) + @unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed') def test_get_form_passing_auth(self): """Ensure GETing form over OAuth with correct client credentials succeed""" -- cgit v1.2.3 From e3aff6a5678d48a2e328c9bb44b7c3de81caffd5 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Fri, 25 Jul 2014 13:38:42 +0000 Subject: Updated test docstring related to missing bearer token. --- rest_framework/tests/test_authentication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/tests/test_authentication.py b/rest_framework/tests/test_authentication.py index cf8415ed..34bf2910 100644 --- a/rest_framework/tests/test_authentication.py +++ b/rest_framework/tests/test_authentication.py @@ -551,7 +551,7 @@ class OAuth2Tests(TestCase): @unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed') def test_get_form_with_wrong_authorization_header_token_missing(self): - """Ensure that a wrong token lead to the correct HTTP error status code""" + """Ensure that a missing token lead to the correct HTTP error status code""" auth = "Bearer" response = self.csrf_client.get('/oauth2-test/', {}, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 401) -- cgit v1.2.3 From fe048dc4fbf064b11d7247061c931bb1038cc774 Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Mon, 28 Jul 2014 07:37:30 +0200 Subject: Fix #1712 (issue when django-guardian is installed but not configured/used) --- rest_framework/compat.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/compat.py b/rest_framework/compat.py index fdf12448..9ad8b0d2 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -48,12 +48,15 @@ try: except ImportError: django_filters = None -# guardian is optional -try: - import guardian - import guardian.shortcuts # Fixes #1624 -except ImportError: - guardian = None +# Django-guardian is optional. Import only if guardian is in INSTALLED_APPS +# Fixes (#1712). We keep the try/except for the test suite. +guardian = None +if 'guardian' in settings.INSTALLED_APPS: + try: + import guardian + import guardian.shortcuts # Fixes #1624 + except ImportError: + pass # cStringIO only if it's available, otherwise StringIO -- cgit v1.2.3 From e40ffd60d44d736d7e27ff454cba1905f0becc26 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 28 Jul 2014 10:11:40 -0700 Subject: Issue #1707 - Add documentation about the caching of `GenericAPIView.queryset` to the `queryset` property, `get_queryset()`, and do generic-views.md; remove changes to the viewsets.md documentation from my last commit. --- rest_framework/generics.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/generics.py b/rest_framework/generics.py index 7bac510f..65ccd952 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -43,6 +43,10 @@ class GenericAPIView(views.APIView): # You'll need to either set these attributes, # or override `get_queryset()`/`get_serializer_class()`. + # If you are overriding a view method, it is important that you call + # `get_queryset()` instead of accessing the `queryset` property directly, + # as `queryset` will get evaluated only once, and those results are cached + # for all subsequent requests. queryset = None serializer_class = None @@ -256,6 +260,10 @@ class GenericAPIView(views.APIView): This must be an iterable, and may be a queryset. Defaults to using `self.queryset`. + This method should always be used rather than accessing `self.queryset` + directly, as `self.queryset` gets evaluated only once, and those results + are cached for all subsequent requests. + You may want to override this if you need to provide different querysets depending on the incoming request. -- cgit v1.2.3 From 4210fedd211eaff80d139a4967577621282f520b Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Tue, 29 Jul 2014 08:35:00 +0200 Subject: Fixed the cache issue with Django 1.7 rc* --- rest_framework/response.py | 1 + 1 file changed, 1 insertion(+) (limited to 'rest_framework') diff --git a/rest_framework/response.py b/rest_framework/response.py index 1dc6abcf..77cbb07c 100644 --- a/rest_framework/response.py +++ b/rest_framework/response.py @@ -15,6 +15,7 @@ class Response(SimpleTemplateResponse): An HttpResponse that allows its data to be rendered into arbitrary media types. """ + rendering_attrs = SimpleTemplateResponse.rendering_attrs + ['_closable_objects'] def __init__(self, data=None, status=200, template_name=None, headers=None, -- cgit v1.2.3 From 59d0a0387d907260ef8f91bbbca618831abd75a3 Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Tue, 29 Jul 2014 10:20:10 +0200 Subject: Fixed the Django 1.3 compat --- rest_framework/response.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/response.py b/rest_framework/response.py index 77cbb07c..3928ca91 100644 --- a/rest_framework/response.py +++ b/rest_framework/response.py @@ -5,6 +5,7 @@ it is initialized with unrendered data, instead of a pre-rendered string. The appropriate renderer is called during Django's template response rendering. """ from __future__ import unicode_literals +import django from django.core.handlers.wsgi import STATUS_CODE_TEXT from django.template.response import SimpleTemplateResponse from rest_framework.compat import six @@ -15,7 +16,9 @@ class Response(SimpleTemplateResponse): An HttpResponse that allows its data to be rendered into arbitrary media types. """ - rendering_attrs = SimpleTemplateResponse.rendering_attrs + ['_closable_objects'] + # TODO: remove that once Django 1.3 isn't supported + if django.VERSION > (1, 3): + rendering_attrs = SimpleTemplateResponse.rendering_attrs + ['_closable_objects'] def __init__(self, data=None, status=200, template_name=None, headers=None, -- cgit v1.2.3 From 5e02f015b81bd743f6ee78f8414eca4e93eaab82 Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Tue, 29 Jul 2014 10:30:08 +0200 Subject: Better fix for the Django 1.3 compat --- rest_framework/response.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/response.py b/rest_framework/response.py index 3928ca91..5c02ea50 100644 --- a/rest_framework/response.py +++ b/rest_framework/response.py @@ -17,7 +17,7 @@ class Response(SimpleTemplateResponse): arbitrary media types. """ # TODO: remove that once Django 1.3 isn't supported - if django.VERSION > (1, 3): + if django.VERSION >= (1, 4): rendering_attrs = SimpleTemplateResponse.rendering_attrs + ['_closable_objects'] def __init__(self, data=None, status=200, -- cgit v1.2.3 From ebcc78d96cf6f4cd6e464cd6b8eccd83305900c2 Mon Sep 17 00:00:00 2001 From: Anler Hp Date: Fri, 1 Aug 2014 10:20:10 +0200 Subject: Leave status responsibility to parent class Django's `HttpResponse` class checks for the `status` param when it's initialized, if it's `None` it uses the class attribute `status_code` and thanks to that we can do things like: ``` class BadRequest(HttpResponse): status_code = 400 ``` Now, that doesn't work when inheriting from rest-framework's `Response`: ``` class BadRequest(rest_framework.response.Response): status_code = 400 # Bad, it's always ignored ``` Because a default status of `200` is being specified in `rest_framework.response.Response`. I think is more Django-friendly to just leave that status default value to `None` and leave the responsibility of choosing its value to the parent class: `HttpResponse`. --- rest_framework/response.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/response.py b/rest_framework/response.py index 5c02ea50..25b78524 100644 --- a/rest_framework/response.py +++ b/rest_framework/response.py @@ -20,7 +20,7 @@ class Response(SimpleTemplateResponse): if django.VERSION >= (1, 4): rendering_attrs = SimpleTemplateResponse.rendering_attrs + ['_closable_objects'] - def __init__(self, data=None, status=200, + def __init__(self, data=None, status=None, template_name=None, headers=None, exception=False, content_type=None): """ -- cgit v1.2.3 From 2d6469348de71f04507f00a5e0e608ae49829dd1 Mon Sep 17 00:00:00 2001 From: Jason Alan Palmer Date: Tue, 5 Aug 2014 10:25:48 -0400 Subject: Remove duplicate class attributes These duplicate attributes are ignored by at least Firefox and Chrome, so this change has no effect on the style--- rest_framework/templates/rest_framework/base.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index 7067ee2f..e96fa8ec 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -93,7 +93,7 @@ {% endif %} {% if options_form %} -
+ {% csrf_token %} @@ -101,7 +101,7 @@ {% endif %} {% if delete_form %} - + {% csrf_token %} -- cgit v1.2.3 From 617745eca027dc17c37718a67f82700caef5be3a Mon Sep 17 00:00:00 2001 From: Kevin London Date: Wed, 6 Aug 2014 16:26:56 -0700 Subject: Update description of OrderingFilter I added a brief description of how you could specify a different query parameter for the OrderingFilter.--- rest_framework/filters.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 96d15eb9..c3b846ae 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -116,6 +116,10 @@ class OrderingFilter(BaseFilterBackend): def get_ordering(self, request): """ Ordering is set by a comma delimited ?ordering=... query parameter. + + The `ordering` query parameter can be overridden by setting + the `ordering_param` value on the OrderingFilter or by + specifying an `ORDERING_PARAM` value in the API settings. """ params = request.QUERY_PARAMS.get(self.ordering_param) if params: -- cgit v1.2.3 From aac864a55f8aec80f35d43052b67c2558814df16 Mon Sep 17 00:00:00 2001 From: Kevin London Date: Thu, 7 Aug 2014 11:02:48 -0700 Subject: Updated documentation for urls.py I made a small change in the order of the documentation for urls.py. I feel it helps make it clear which lines you should add to the root settings.--- rest_framework/urls.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/urls.py b/rest_framework/urls.py index 9c4719f1..5d70f899 100644 --- a/rest_framework/urls.py +++ b/rest_framework/urls.py @@ -2,15 +2,15 @@ Login and logout views for the browsable API. Add these to your root URLconf if you're using the browsable API and -your API requires authentication. - -The urls must be namespaced as 'rest_framework', and you should make sure -your authentication settings include `SessionAuthentication`. +your API requires authentication: urlpatterns = patterns('', ... url(r'^auth', include('rest_framework.urls', namespace='rest_framework')) ) + +The urls must be namespaced as 'rest_framework', and you should make sure +your authentication settings include `SessionAuthentication`. """ from __future__ import unicode_literals from rest_framework.compat import patterns, url -- cgit v1.2.3 From 09c53bbac9205b46bafd77e456b495c63c9d46ef Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 11 Aug 2014 16:20:27 +0100 Subject: Refactor JSONRenderer slightly for easier overriding --- rest_framework/renderers.py | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 484961ad..7048d87d 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -54,32 +54,37 @@ class JSONRenderer(BaseRenderer): format = 'json' encoder_class = encoders.JSONEncoder ensure_ascii = True - charset = None - # JSON is a binary encoding, that can be encoded as utf-8, utf-16 or utf-32. + + # We don't set a charset because JSON is a binary encoding, + # that can be encoded as utf-8, utf-16 or utf-32. # See: http://www.ietf.org/rfc/rfc4627.txt # Also: http://lucumr.pocoo.org/2013/7/19/application-mimetypes-and-encodings/ + charset = None + + def get_indent(self, accepted_media_type, renderer_context): + if accepted_media_type: + # If the media type looks like 'application/json; indent=4', + # then pretty print the result. + base_media_type, params = parse_header(accepted_media_type.encode('ascii')) + try: + return max(min(int(params['indent']), 8), 0) + except (KeyError, ValueError, TypeError): + pass + + # If 'indent' is provided in the context, then pretty print the result. + # E.g. If we're being called by the BrowsableAPIRenderer. + return renderer_context.get('indent', None) + def render(self, data, accepted_media_type=None, renderer_context=None): """ - Render `data` into JSON. + Render `data` into JSON, returning a bytestring. """ if data is None: return bytes() - # If 'indent' is provided in the context, then pretty print the result. - # E.g. If we're being called by the BrowsableAPIRenderer. renderer_context = renderer_context or {} - indent = renderer_context.get('indent', None) - - if accepted_media_type: - # If the media type looks like 'application/json; indent=4', - # then pretty print the result. - base_media_type, params = parse_header(accepted_media_type.encode('ascii')) - indent = params.get('indent', indent) - try: - indent = max(min(int(indent), 8), 0) - except (ValueError, TypeError): - indent = None + indent = self.get_indent(accepted_media_type, renderer_context) ret = json.dumps(data, cls=self.encoder_class, indent=indent, ensure_ascii=self.ensure_ascii) -- cgit v1.2.3 From 34c1da3515dbf4e9a0d645ebf81cde6f61254e31 Mon Sep 17 00:00:00 2001 From: John Whitlock Date: Wed, 13 Aug 2014 15:31:25 -0500 Subject: ModelSerializer.restore_object - errors as list When a ValueError is raised in ModelSerializer.restore_object, the error is set to a one-element list, rather than a bare string. --- rest_framework/serializers.py | 2 +- rest_framework/tests/test_serializer.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index c2b414d7..43d339da 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -977,7 +977,7 @@ class ModelSerializer(Serializer): try: setattr(instance, key, val) except ValueError: - self._errors[key] = self.error_messages['required'] + self._errors[key] = [self.error_messages['required']] # Any relations that cannot be set until we've # saved the model get hidden away on these diff --git a/rest_framework/tests/test_serializer.py b/rest_framework/tests/test_serializer.py index 91248ce7..fb2eac0b 100644 --- a/rest_framework/tests/test_serializer.py +++ b/rest_framework/tests/test_serializer.py @@ -685,7 +685,7 @@ class ModelValidationTests(TestCase): photo_serializer = PhotoSerializer(instance=photo, data={'album': ''}, partial=True) self.assertFalse(photo_serializer.is_valid()) self.assertTrue('album' in photo_serializer.errors) - self.assertEqual(photo_serializer.errors['album'], photo_serializer.error_messages['required']) + self.assertEqual(photo_serializer.errors['album'], [photo_serializer.error_messages['required']]) def test_foreign_key_with_partial(self): """ -- cgit v1.2.3 From a6901ea36de19a35fa82783c984841b4c3ca0dad Mon Sep 17 00:00:00 2001 From: Aymeric Derbois Date: Sat, 16 Aug 2014 15:49:31 +0200 Subject: Add test for SerializerMethodField --- rest_framework/tests/test_fields.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/tests/test_fields.py b/rest_framework/tests/test_fields.py index b04b947f..17d12f23 100644 --- a/rest_framework/tests/test_fields.py +++ b/rest_framework/tests/test_fields.py @@ -1002,3 +1002,21 @@ class BooleanField(TestCase): bool_field = serializers.BooleanField(required=True) self.assertFalse(BooleanRequiredSerializer(data={}).is_valid()) + + +class SerializerMethodFieldTest(TestCase): + """ + Tests for the SerializerMethodField field_to_native() behavior + """ + class SerializerTest(serializers.Serializer): + def get_my_test(self, obj): + return obj.my_test[0:5] + + class Example(): + my_test = 'Hey, this is a test !' + + def test_field_to_native(self): + s = serializers.SerializerMethodField('get_my_test') + s.initialize(self.SerializerTest(), 'name') + result = s.field_to_native(self.Example(), None) + self.assertEqual(result, 'Hey, ') -- cgit v1.2.3 From 5f63d31b002020148c526f2d5ec27f723a06f2e9 Mon Sep 17 00:00:00 2001 From: Andrew Fong Date: Sat, 16 Aug 2014 15:05:46 -0700 Subject: override_method should substitute action A view's action is dependent on the request method. When overriding the method (e.g. to generate a form for a POST request on a GET call to the browseable API), the action should be updated as well. Otherwise, viewset functions may be in a weird limbo state where a 'list' action has a POST method.--- rest_framework/request.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/request.py b/rest_framework/request.py index 40467c03..1ea61586 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -42,12 +42,16 @@ class override_method(object): self.view = view self.request = request self.method = method + self.action = getattr(view, 'action', None) def __enter__(self): self.view.request = clone_request(self.request, self.method) + action_map = getattr(self, 'action_map', {}) + self.view.action = action_map.get(self.method.lower()) return self.view.request def __exit__(self, *args, **kwarg): + self.view.action = self.action self.view.request = self.request -- cgit v1.2.3 From 21cbf3484e04bb015c1921307cdf0306a81e571d Mon Sep 17 00:00:00 2001 From: Andrew Fong Date: Sat, 16 Aug 2014 23:22:18 +0000 Subject: Fixed action_map being pulled from wrong object --- rest_framework/request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/request.py b/rest_framework/request.py index 1ea61586..735a9288 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -46,7 +46,7 @@ class override_method(object): def __enter__(self): self.view.request = clone_request(self.request, self.method) - action_map = getattr(self, 'action_map', {}) + action_map = getattr(self.view, 'action_map', {}) self.view.action = action_map.get(self.method.lower()) return self.view.request -- cgit v1.2.3 From 33af92e019fda70af7a4138972ee9780a9cc967a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 18 Aug 2014 15:14:30 +0100 Subject: Always uppercase X-Http-Method-Override methods. Closes #1718. --- rest_framework/request.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/request.py b/rest_framework/request.py index 40467c03..dc696e36 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -280,8 +280,8 @@ class Request(object): self._method = self._request.method # Allow X-HTTP-METHOD-OVERRIDE header - self._method = self.META.get('HTTP_X_HTTP_METHOD_OVERRIDE', - self._method) + if 'HTTP_X_HTTP_METHOD_OVERRIDE' in self.META: + self._method = self.META['HTTP_X_HTTP_METHOD_OVERRIDE'].upper() def _load_stream(self): """ -- cgit v1.2.3 From 9f3c7e8930dd86e13efff8b2de1710f47fb74d96 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 18 Aug 2014 15:34:23 +0100 Subject: Copy filter_backends class attribute before returning it. --- rest_framework/generics.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/generics.py b/rest_framework/generics.py index 42204841..aea636f1 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -189,7 +189,13 @@ class GenericAPIView(views.APIView): """ Returns the list of filter backends that this view requires. """ - filter_backends = self.filter_backends or [] + if self.filter_backends is None: + filter_backends = [] + else: + # Note that we are returning a *copy* of the class attribute, + # so that it is safe for the view to mutate it if needed. + filter_backends = list(self.filter_backends) + if not filter_backends and self.filter_backend: warnings.warn( 'The `filter_backend` attribute and `FILTER_BACKEND` setting ' @@ -199,6 +205,7 @@ class GenericAPIView(views.APIView): PendingDeprecationWarning, stacklevel=2 ) filter_backends = [self.filter_backend] + return filter_backends -- cgit v1.2.3 From 97d8f037cc1292407eae965ceb4df34a52bd6161 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 18 Aug 2014 20:56:17 +0100 Subject: Only set .action attribute in override_method if it already existed on the view --- rest_framework/request.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/request.py b/rest_framework/request.py index 4f9345f3..d508f9b4 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -46,13 +46,16 @@ class override_method(object): def __enter__(self): self.view.request = clone_request(self.request, self.method) - action_map = getattr(self.view, 'action_map', {}) - self.view.action = action_map.get(self.method.lower()) + if self.action is not None: + # For viewsets we also set the `.action` attribute. + action_map = getattr(self.view, 'action_map', {}) + self.view.action = action_map.get(self.method.lower()) return self.view.request def __exit__(self, *args, **kwarg): - self.view.action = self.action self.view.request = self.request + if self.action is not None: + self.view.action = self.action class Empty(object): -- cgit v1.2.3