diff options
| -rwxr-xr-x | docs/api-guide/generic-views.md | 3 | ||||
| -rw-r--r-- | docs/topics/credits.md | 2 | ||||
| -rw-r--r-- | docs/topics/release-notes.md | 2 | ||||
| -rw-r--r-- | rest_framework/authentication.py | 9 | ||||
| -rw-r--r-- | rest_framework/authtoken/models.py | 4 | ||||
| -rw-r--r-- | rest_framework/compat.py | 20 | ||||
| -rw-r--r-- | rest_framework/runtests/settings.py | 2 | ||||
| -rw-r--r-- | rest_framework/serializers.py | 5 | ||||
| -rw-r--r-- | rest_framework/tests/test_description.py | 33 | ||||
| -rw-r--r-- | rest_framework/utils/formatting.py | 4 |
10 files changed, 59 insertions, 25 deletions
diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index cd1bc7a1..67853ed0 100755 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -92,7 +92,8 @@ May be overridden to provide dynamic behavior such as returning a queryset that For example: def get_queryset(self): - return self.user.accounts.all() + user = self.request.user + return user.accounts.all() #### `get_object(self)` diff --git a/docs/topics/credits.md b/docs/topics/credits.md index a7c09b5b..94760c74 100644 --- a/docs/topics/credits.md +++ b/docs/topics/credits.md @@ -143,6 +143,7 @@ The following people have helped make REST framework great. * Ethan Freman - [mindlace] * David Sanders - [davesque] * Philip Douglas - [freakydug] +* Igor Kalat - [trwired] Many thanks to everyone who's contributed to the project. @@ -322,3 +323,4 @@ You can also contact [@_tomchristie][twitter] directly on twitter. [mindlace]: https://github.com/mindlace [davesque]: https://github.com/davesque [freakydug]: https://github.com/freakydug +[trwired]: https://github.com/trwired diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index b08ac058..4fecbf1f 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -45,6 +45,8 @@ You can determine your currently installed version using `pip freeze`: * Added `trailing_slash` option to routers. * Include support for `HttpStreamingResponse`. * Support wider range of default serializer validation when used with custom model fields. +* UTF-8 Support for browsable API descriptions. +* OAuth2 provider usez timezone aware datetimes when supported. * Bugfix: Return error correctly when OAuth non-existent consumer occurs. * Bugfix: Allow `FileUploadParser` to correctly filename if provided as URL kwarg. * Bugfix: Fix `ScopedRateThrottle`. diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index f659a172..10298027 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -3,14 +3,13 @@ Provides various authentication policies. """ from __future__ import unicode_literals import base64 -from datetime import datetime from django.contrib.auth import authenticate from django.core.exceptions import ImproperlyConfigured from rest_framework import exceptions, HTTP_HEADER_ENCODING from rest_framework.compat import CsrfViewMiddleware from rest_framework.compat import oauth, oauth_provider, oauth_provider_store -from rest_framework.compat import oauth2_provider +from rest_framework.compat import oauth2_provider, provider_now from rest_framework.authtoken.models import Token @@ -320,9 +319,9 @@ class OAuth2Authentication(BaseAuthentication): try: token = oauth2_provider.models.AccessToken.objects.select_related('user') - # TODO: Change to timezone aware datetime when oauth2_provider add - # support to it. - token = token.get(token=access_token, expires__gt=datetime.now()) + # provider_now switches to timezone aware datetime when + # the oauth2_provider version supports to it. + token = token.get(token=access_token, expires__gt=provider_now()) except oauth2_provider.models.AccessToken.DoesNotExist: raise exceptions.AuthenticationFailed('Invalid token') diff --git a/rest_framework/authtoken/models.py b/rest_framework/authtoken/models.py index 52c45ad1..7601f5b7 100644 --- a/rest_framework/authtoken/models.py +++ b/rest_framework/authtoken/models.py @@ -1,7 +1,7 @@ import uuid import hmac from hashlib import sha1 -from rest_framework.compat import User +from rest_framework.compat import AUTH_USER_MODEL from django.conf import settings from django.db import models @@ -11,7 +11,7 @@ class Token(models.Model): The default authorization token model. """ key = models.CharField(max_length=40, primary_key=True) - user = models.OneToOneField(User, related_name='auth_token') + user = models.OneToOneField(AUTH_USER_MODEL, related_name='auth_token') created = models.DateTimeField(auto_now_add=True) class Meta: diff --git a/rest_framework/compat.py b/rest_framework/compat.py index a19bd778..b748dcc5 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -2,6 +2,7 @@ The `compat` module provides support for backwards compatibility with older versions of django/python, and compatibility wrappers around optional packages. """ + # flake8: noqa from __future__ import unicode_literals @@ -83,15 +84,9 @@ def get_concrete_model(model_cls): # Django 1.5 add support for custom auth user model if django.VERSION >= (1, 5): from django.conf import settings - if hasattr(settings, 'AUTH_USER_MODEL'): - User = settings.AUTH_USER_MODEL - else: - from django.contrib.auth.models import User + AUTH_USER_MODEL = settings.AUTH_USER_MODEL else: - try: - from django.contrib.auth.models import User - except ImportError: - raise ImportError("User model is not to be found.") + AUTH_USER_MODEL = 'auth.User' if django.VERSION >= (1, 5): @@ -495,12 +490,21 @@ try: from provider.oauth2 import forms as oauth2_provider_forms from provider import scope as oauth2_provider_scope from provider import constants as oauth2_constants + from provider import __version__ as provider_version + if provider_version in ('0.2.3', '0.2.4'): + # 0.2.3 and 0.2.4 are supported version that do not support + # timezone aware datetimes + from datetime.datetime import now as provider_now + else: + # Any other supported version does use timezone aware datetimes + from django.utils.timezone import now as provider_now except ImportError: oauth2_provider = None oauth2_provider_models = None oauth2_provider_forms = None oauth2_provider_scope = None oauth2_constants = None + provider_now = None # Handle lazy strings from django.utils.functional import Promise diff --git a/rest_framework/runtests/settings.py b/rest_framework/runtests/settings.py index 9dd7b545..b3702d0b 100644 --- a/rest_framework/runtests/settings.py +++ b/rest_framework/runtests/settings.py @@ -134,6 +134,8 @@ PASSWORD_HASHERS = ( 'django.contrib.auth.hashers.CryptPasswordHasher', ) +AUTH_USER_MODEL = 'auth.User' + import django if django.VERSION < (1, 3): diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 5a8fd89f..023f7ccf 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -915,7 +915,10 @@ class HyperlinkedModelSerializer(ModelSerializer): view_name=self.opts.view_name, lookup_field=self.opts.lookup_field ) - fields.insert(0, 'url', url_field) + ret = self._dict_class() + ret['url'] = url_field + ret.update(fields) + fields = ret return fields diff --git a/rest_framework/tests/test_description.py b/rest_framework/tests/test_description.py index 52c1a34c..ea4b2c3a 100644 --- a/rest_framework/tests/test_description.py +++ b/rest_framework/tests/test_description.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from django.test import TestCase from rest_framework.views import APIView -from rest_framework.compat import apply_markdown +from rest_framework.compat import apply_markdown, smart_text from rest_framework.utils.formatting import get_view_name, get_view_description # We check that docstrings get nicely un-indented. @@ -49,6 +49,28 @@ MARKED_DOWN_gte_21 = """<h2 id="an-example-docstring">an example docstring</h2> <h2 id="hash-style-header">hash style header</h2>""" +# test strings snatched from http://www.columbia.edu/~fdc/utf8/, +# http://winrus.com/utf8-jap.htm and memory +UTF8_TEST_DOCSTRING = ( + 'zażółć gęślą jaźń' + 'Sîne klâwen durh die wolken sint geslagen' + 'Τη γλώσσα μου έδωσαν ελληνική' + 'யாமறிந்த மொழிகளிலே தமிழ்மொழி' + 'На берегу пустынных волн' + 'てすと' + 'アイウエオカキクケコサシスセソタチツテ' +) + + +# Apparently there is an issue where docstrings of imported view classes +# do not retain their encoding information even if a module has a proper +# encoding declaration at the top of its source file. Therefore for tests +# to catch unicode related errors, a mock view has to be declared in a separate +# module. +class ViewWithNonASCIICharactersInDocstring(APIView): + __doc__ = UTF8_TEST_DOCSTRING + + class TestViewNamesAndDescriptions(TestCase): def test_view_name_uses_class_name(self): """ @@ -83,11 +105,10 @@ class TestViewNamesAndDescriptions(TestCase): Unicode in docstrings should be respected. """ - class MockView(APIView): - """Проверка""" - pass - - self.assertEqual(get_view_description(MockView), "Проверка") + self.assertEqual( + get_view_description(ViewWithNonASCIICharactersInDocstring), + smart_text(UTF8_TEST_DOCSTRING) + ) def test_view_description_can_be_empty(self): """ diff --git a/rest_framework/utils/formatting.py b/rest_framework/utils/formatting.py index ebadb3a6..4bec8387 100644 --- a/rest_framework/utils/formatting.py +++ b/rest_framework/utils/formatting.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals from django.utils.html import escape from django.utils.safestring import mark_safe -from rest_framework.compat import apply_markdown +from rest_framework.compat import apply_markdown, smart_text import re @@ -63,7 +63,7 @@ def get_view_description(cls, html=False): Return a description for an `APIView` class or `@api_view` function. """ description = cls.__doc__ or '' - description = _remove_leading_indent(description) + description = _remove_leading_indent(smart_text(description)) if html: return markup_description(description) return description |
