diff --git a/rest_framework/compat.py b/rest_framework/compat.py index c5242343e..3c8fb0da4 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -5,15 +5,13 @@ versions of django/python, and compatibility wrappers around optional packages. # flake8: noqa from __future__ import unicode_literals - -import inspect - from django.core.exceptions import ImproperlyConfigured +from django.conf import settings from django.utils.encoding import force_text from django.utils.six.moves.urllib import parse as urlparse -from django.conf import settings from django.utils import six import django +import inspect def unicode_repr(instance): @@ -33,6 +31,13 @@ def unicode_to_repr(value): return value +def unicode_http_header(value): + # Coerce HTTP header value to unicode. + if isinstance(value, six.binary_type): + return value.decode('iso-8859-1') + return value + + # OrderedDict only available in Python 2.7. # This will always be the case in Django 1.7 and above, as these versions # no longer support Python 2.6. diff --git a/rest_framework/versioning.py b/rest_framework/versioning.py index 42df8b2c0..9a27cb081 100644 --- a/rest_framework/versioning.py +++ b/rest_framework/versioning.py @@ -1,5 +1,6 @@ # coding: utf-8 from __future__ import unicode_literals +from rest_framework.compat import unicode_http_header from rest_framework.reverse import _reverse from rest_framework.templatetags.rest_framework import replace_query_param from rest_framework.utils.mediatypes import _MediaType @@ -69,7 +70,8 @@ class AcceptHeaderVersioning(BaseVersioning): def determine_version(self, request, *args, **kwargs): media_type = _MediaType(request.accepted_media_type) - return media_type.params.get(self.version_param, self.default_version) + version = media_type.params.get(self.version_param, self.default_version) + return unicode_http_header(version) # We don't need to implement `reverse`, as the versioning is based # on the `Accept` header, not on the request URL. @@ -77,6 +79,17 @@ class AcceptHeaderVersioning(BaseVersioning): class URLPathVersioning(BaseVersioning): """ + To the client this is the same style as `NamespaceVersioning`. + The difference is in the backend - this implementation uses + Django's URL keyword arguments to determine the version. + + An example URL conf for two views that accept two different versions. + + urlpatterns = [ + url(r'^(?P{v1,v2})/users/$', users_list, name='users-list'), + url(r'^(?P{v1,v2})/users/(?P[0-9]+)/$', users_detail, name='users-detail') + ] + GET /1.0/something/ HTTP/1.1 Host: example.com Accept: application/json @@ -103,6 +116,20 @@ class NamespaceVersioning(BaseVersioning): The difference is in the backend - this implementation uses Django's URL namespaces to determine the version. + An example URL conf that is namespaced into two seperate versions + + # users/urls.py + urlpatterns = [ + url(r'^/users/$', users_list, name='users-list'), + url(r'^/users/(?P[0-9]+)/$', users_detail, name='users-detail') + ] + + # urls.py + urlpatterns = [ + url(r'^v1/', include('users.urls', namespace='v1')), + url(r'^v2/', include('users.urls', namespace='v2')) + ] + GET /1.0/something/ HTTP/1.1 Host: example.com Accept: application/json