aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Christie2013-02-04 20:37:09 +0000
committerTom Christie2013-02-04 20:37:09 +0000
commit8e846bdf52f03d0733ac73e418152800e582b898 (patch)
treecd7034d5573f1e264798a618c88addc4d2f45ce0
parentd9b73e15c87c3a7f11d6bea5ffd6118f86e40051 (diff)
parent97f2b994951605ffdef08159be450d1e77762bf9 (diff)
downloaddjango-rest-framework-8e846bdf52f03d0733ac73e418152800e582b898.tar.bz2
Merge branch 'py3k' into 2.2
Conflicts: rest_framework/relations.py rest_framework/serializers.py rest_framework/tests/relations_hyperlink.py rest_framework/tests/relations_slug.py
-rw-r--r--.travis.yml22
-rw-r--r--README.md2
-rw-r--r--docs/index.md2
-rw-r--r--docs/topics/credits.md2
-rw-r--r--rest_framework/__init__.py3
-rw-r--r--rest_framework/authentication.py22
-rw-r--r--rest_framework/authtoken/models.py4
-rw-r--r--rest_framework/compat.py46
-rw-r--r--rest_framework/fields.py51
-rw-r--r--rest_framework/filters.py2
-rw-r--r--rest_framework/mixins.py4
-rw-r--r--rest_framework/negotiation.py4
-rw-r--r--rest_framework/parsers.py25
-rw-r--r--rest_framework/relations.py22
-rw-r--r--rest_framework/renderers.py10
-rw-r--r--rest_framework/request.py9
-rw-r--r--rest_framework/response.py6
-rwxr-xr-xrest_framework/runtests/runtests.py2
-rw-r--r--rest_framework/serializers.py23
-rw-r--r--rest_framework/settings.py3
-rw-r--r--rest_framework/six.py389
-rw-r--r--rest_framework/templatetags/rest_framework.py18
-rw-r--r--rest_framework/tests/authentication.py16
-rw-r--r--rest_framework/tests/files.py7
-rw-r--r--rest_framework/tests/genericrelations.py18
-rw-r--r--rest_framework/tests/generics.py11
-rw-r--r--rest_framework/tests/htmlrenderer.py9
-rw-r--r--rest_framework/tests/negotiation.py8
-rw-r--r--rest_framework/tests/parsers.py2
-rw-r--r--rest_framework/tests/relations_hyperlink.py172
-rw-r--r--rest_framework/tests/relations_nested.py27
-rw-r--r--rest_framework/tests/relations_pk.py173
-rw-r--r--rest_framework/tests/relations_slug.py108
-rw-r--r--rest_framework/tests/renderers.py18
-rw-r--r--rest_framework/tests/request.py11
-rw-r--r--rest_framework/tests/response.py7
-rw-r--r--rest_framework/tests/serializer.py54
-rw-r--r--rest_framework/tests/utils.py4
-rw-r--r--rest_framework/tests/validators.py2
-rw-r--r--rest_framework/tests/views.py10
-rw-r--r--rest_framework/utils/__init__.py9
-rw-r--r--rest_framework/utils/mediatypes.py3
-rwxr-xr-xsetup.py9
-rw-r--r--tox.ini34
44 files changed, 953 insertions, 430 deletions
diff --git a/.travis.yml b/.travis.yml
index 0dc87837..662ea5c8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,16 +3,30 @@ language: python
python:
- "2.6"
- "2.7"
+ - "3.2"
+ - "3.3"
env:
- - DJANGO=https://github.com/django/django/zipball/master
- - DJANGO=django==1.4.3 --use-mirrors
- - DJANGO=django==1.3.5 --use-mirrors
+ - DJANGO=https://www.djangoproject.com/download/1.5c1/tarball/
+ - DJANGO="django==1.4.3 --use-mirrors"
+ - DJANGO="django==1.3.5 --use-mirrors"
install:
- pip install $DJANGO
- - pip install django-filter==0.5.4 --use-mirrors
+ - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-filter==0.5.4 --use-mirrors; fi"
+ - "if [[ ${TRAVIS_PYTHON_VERSION::1} == '3' ]]; then pip install https://github.com/alex/django-filter/tarball/master; fi"
- export PYTHONPATH=.
script:
- python rest_framework/runtests/runtests.py
+
+matrix:
+ exclude:
+ - python: "3.2"
+ env: DJANGO="django==1.4.3 --use-mirrors"
+ - python: "3.2"
+ env: DJANGO="django==1.3.5 --use-mirrors"
+ - python: "3.3"
+ env: DJANGO="django==1.4.3 --use-mirrors"
+ - python: "3.3"
+ env: DJANGO="django==1.3.5 --use-mirrors"
diff --git a/README.md b/README.md
index 523b7e74..42c25c63 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ There is also a sandbox API you can use for testing purposes, [available here][s
# Requirements
-* Python (2.6, 2.7)
+* Python (2.6, 2.7, 3.2, 3.3)
* Django (1.3, 1.4, 1.5)
**Optional:**
diff --git a/docs/index.md b/docs/index.md
index 453a67b8..283ea069 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -33,7 +33,7 @@ There is also a sandbox API you can use for testing purposes, [available here][s
REST framework requires the following:
-* Python (2.6, 2.7)
+* Python (2.6, 2.7, 3.2, 3.3)
* Django (1.3, 1.4, 1.5)
The following packages are optional:
diff --git a/docs/topics/credits.md b/docs/topics/credits.md
index a67a8169..ebca4491 100644
--- a/docs/topics/credits.md
+++ b/docs/topics/credits.md
@@ -101,6 +101,7 @@ The following people have helped make REST framework great.
* MichaƂ Jaworski - [swistakm]
* Andrea de Marco - [z4r]
* Fernando Rocha - [fernandogrd]
+* Xavier Ordoquy - [xordoquy]
Many thanks to everyone who's contributed to the project.
@@ -237,3 +238,4 @@ You can also contact [@_tomchristie][twitter] directly on twitter.
[swistakm]: https://github.com/swistakm
[z4r]: https://github.com/z4r
[fernandogrd]: https://github.com/fernandogrd
+[xordoquy]: https://github.com/xordoquy
diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py
index f9882c57..80e2c410 100644
--- a/rest_framework/__init__.py
+++ b/rest_framework/__init__.py
@@ -1,3 +1,6 @@
__version__ = '2.1.17'
VERSION = __version__ # synonym
+
+# Header encoding (see RFC5987)
+HTTP_HEADER_ENCODING = 'iso-8859-1'
diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py
index fc169189..c15568db 100644
--- a/rest_framework/authentication.py
+++ b/rest_framework/authentication.py
@@ -1,12 +1,15 @@
"""
Provides a set of pluggable authentication policies.
"""
+from __future__ import unicode_literals
from django.contrib.auth import authenticate
-from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError
-from rest_framework import exceptions
+from django.utils.encoding import DjangoUnicodeDecodeError
+from rest_framework import exceptions, HTTP_HEADER_ENCODING
from rest_framework.compat import CsrfViewMiddleware
+from rest_framework.compat import smart_text
from rest_framework.authtoken.models import Token
+from rest_framework.settings import api_settings
import base64
@@ -41,22 +44,25 @@ class BasicAuthentication(BaseAuthentication):
Returns a `User` if a correct username and password have been supplied
using HTTP Basic authentication. Otherwise returns `None`.
"""
- auth = request.META.get('HTTP_AUTHORIZATION', '').split()
+ auth = request.META.get('HTTP_AUTHORIZATION', b'')
+ if type(auth) == type(''):
+ # Work around django test client oddness
+ auth = auth.encode(HTTP_HEADER_ENCODING)
+ auth = auth.split()
- if not auth or auth[0].lower() != "basic":
+ if not auth or auth[0].lower() != b'basic':
return None
if len(auth) != 2:
raise exceptions.AuthenticationFailed('Invalid basic header')
try:
- auth_parts = base64.b64decode(auth[1]).partition(':')
- except TypeError:
+ auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')
+ except (TypeError, UnicodeDecodeError):
raise exceptions.AuthenticationFailed('Invalid basic header')
try:
- userid = smart_unicode(auth_parts[0])
- password = smart_unicode(auth_parts[2])
+ userid, password = auth_parts[0], auth_parts[2]
except DjangoUnicodeDecodeError:
raise exceptions.AuthenticationFailed('Invalid basic header')
diff --git a/rest_framework/authtoken/models.py b/rest_framework/authtoken/models.py
index 4da2aa62..7f5a75a3 100644
--- a/rest_framework/authtoken/models.py
+++ b/rest_framework/authtoken/models.py
@@ -19,8 +19,8 @@ class Token(models.Model):
return super(Token, self).save(*args, **kwargs)
def generate_key(self):
- unique = str(uuid.uuid4())
- return hmac.new(unique, digestmod=sha1).hexdigest()
+ unique = uuid.uuid4()
+ return hmac.new(unique.bytes, digestmod=sha1).hexdigest()
def __unicode__(self):
return self.key
diff --git a/rest_framework/compat.py b/rest_framework/compat.py
index 5508f6c0..8c64d951 100644
--- a/rest_framework/compat.py
+++ b/rest_framework/compat.py
@@ -3,14 +3,35 @@ 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
+
import django
+# Try to import six from Django, fallback to included `six`.
+try:
+ from django.utils import six
+except:
+ from rest_framework import six
+
# location of patterns, url, include changes in 1.4 onwards
try:
from django.conf.urls import patterns, url, include
except:
from django.conf.urls.defaults import patterns, url, include
+# Handle django.utils.encoding rename:
+# smart_unicode -> smart_text
+# force_unicode -> force_text
+try:
+ from django.utils.encoding import smart_text
+except ImportError:
+ from django.utils.encoding import smart_unicode as smart_text
+try:
+ from django.utils.encoding import force_text
+except ImportError:
+ from django.utils.encoding import force_unicode as force_text
+
+
# django-filter is optional
try:
import django_filters
@@ -20,9 +41,18 @@ except:
# cStringIO only if it's available, otherwise StringIO
try:
- import cStringIO as StringIO
+ import cStringIO.StringIO as StringIO
except ImportError:
- import StringIO
+ StringIO = six.StringIO
+
+BytesIO = six.BytesIO
+
+
+# urlparse compat import (Required because it changed in python 3.x)
+try:
+ from urllib import parse as urlparse
+except ImportError:
+ import urlparse
# Try to import PIL in either of the two ways it can end up installed.
@@ -54,7 +84,7 @@ else:
try:
from django.contrib.auth.models import User
except ImportError:
- raise ImportError(u"User model is not to be found.")
+ raise ImportError("User model is not to be found.")
# First implementation of Django class-based views did not include head method
@@ -75,11 +105,11 @@ else:
# sanitize keyword arguments
for key in initkwargs:
if key in cls.http_method_names:
- raise TypeError(u"You tried to pass in the %s method name as a "
- u"keyword argument to %s(). Don't do that."
+ raise TypeError("You tried to pass in the %s method name as a "
+ "keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
- raise TypeError(u"%s() received an invalid keyword %r" % (
+ raise TypeError("%s() received an invalid keyword %r" % (
cls.__name__, key))
def view(request, *args, **kwargs):
@@ -110,7 +140,6 @@ else:
import re
import random
import logging
- import urlparse
from django.conf import settings
from django.core.urlresolvers import get_callable
@@ -152,7 +181,8 @@ else:
randrange = random.SystemRandom().randrange
else:
randrange = random.randrange
- _MAX_CSRF_KEY = 18446744073709551616L # 2 << 63
+
+ _MAX_CSRF_KEY = 18446744073709551616 # 2 << 63
REASON_NO_REFERER = "Referer checking failed - no Referer."
REASON_BAD_REFERER = "Referer checking failed - %s does not match %s."
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index b9b7e426..d6e8539d 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -1,20 +1,23 @@
+from __future__ import unicode_literals
+
import copy
import datetime
import inspect
import re
import warnings
-from io import BytesIO
-
from django.core import validators
from django.core.exceptions import ValidationError
from django.conf import settings
from django import forms
from django.forms import widgets
-from django.utils.encoding import is_protected_type, smart_unicode
+from django.utils.encoding import is_protected_type
from django.utils.translation import ugettext_lazy as _
from rest_framework.compat import parse_date, parse_datetime
from rest_framework.compat import timezone
+from rest_framework.compat import BytesIO
+from rest_framework.compat import six
+from rest_framework.compat import smart_text
def is_simple_callable(obj):
@@ -95,11 +98,11 @@ class Field(object):
if is_protected_type(value):
return value
- elif hasattr(value, '__iter__') and not isinstance(value, (dict, basestring)):
+ elif hasattr(value, '__iter__') and not isinstance(value, (dict, six.string_types)):
return [self.to_native(item) for item in value]
elif isinstance(value, dict):
return dict(map(self.to_native, (k, v)) for k, v in value.items())
- return smart_unicode(value)
+ return smart_text(value)
def attributes(self):
"""
@@ -266,7 +269,7 @@ class BooleanField(WritableField):
form_field_class = forms.BooleanField
widget = widgets.CheckboxInput
default_error_messages = {
- 'invalid': _(u"'%s' value must be either True or False."),
+ 'invalid': _("'%s' value must be either True or False."),
}
empty = False
@@ -296,9 +299,9 @@ class CharField(WritableField):
self.validators.append(validators.MaxLengthValidator(max_length))
def from_native(self, value):
- if isinstance(value, basestring) or value is None:
+ if isinstance(value, six.string_types) or value is None:
return value
- return smart_unicode(value)
+ return smart_text(value)
class URLField(CharField):
@@ -358,10 +361,10 @@ class ChoiceField(WritableField):
if isinstance(v, (list, tuple)):
# This is an optgroup, so look inside the group for options
for k2, v2 in v:
- if value == smart_unicode(k2):
+ if value == smart_text(k2):
return True
else:
- if value == smart_unicode(k) or value == k:
+ if value == smart_text(k) or value == k:
return True
return False
@@ -401,7 +404,7 @@ class RegexField(CharField):
return self._regex
def _set_regex(self, regex):
- if isinstance(regex, basestring):
+ if isinstance(regex, six.string_types):
regex = re.compile(regex)
self._regex = regex
if hasattr(self, '_regex_validator') and self._regex_validator in self.validators:
@@ -424,10 +427,10 @@ class DateField(WritableField):
form_field_class = forms.DateField
default_error_messages = {
- 'invalid': _(u"'%s' value has an invalid date format. It must be "
- u"in YYYY-MM-DD format."),
- 'invalid_date': _(u"'%s' value has the correct format (YYYY-MM-DD) "
- u"but it is an invalid date."),
+ 'invalid': _("'%s' value has an invalid date format. It must be "
+ "in YYYY-MM-DD format."),
+ 'invalid_date': _("'%s' value has the correct format (YYYY-MM-DD) "
+ "but it is an invalid date."),
}
empty = None
@@ -463,13 +466,13 @@ class DateTimeField(WritableField):
form_field_class = forms.DateTimeField
default_error_messages = {
- 'invalid': _(u"'%s' value has an invalid format. It must be in "
- u"YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."),
- 'invalid_date': _(u"'%s' value has the correct format "
- u"(YYYY-MM-DD) but it is an invalid date."),
- 'invalid_datetime': _(u"'%s' value has the correct format "
- u"(YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) "
- u"but it is an invalid date/time."),
+ 'invalid': _("'%s' value has an invalid format. It must be in "
+ "YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."),
+ 'invalid_date': _("'%s' value has the correct format "
+ "(YYYY-MM-DD) but it is an invalid date."),
+ 'invalid_datetime': _("'%s' value has the correct format "
+ "(YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) "
+ "but it is an invalid date/time."),
}
empty = None
@@ -486,8 +489,8 @@ class DateTimeField(WritableField):
# local time. This won't work during DST change, but we can't
# do much about it, so we let the exceptions percolate up the
# call stack.
- warnings.warn(u"DateTimeField received a naive datetime (%s)"
- u" while time zone support is active." % value,
+ warnings.warn("DateTimeField received a naive datetime (%s)"
+ " while time zone support is active." % value,
RuntimeWarning)
default_timezone = timezone.get_default_timezone()
value = timezone.make_aware(value, default_timezone)
diff --git a/rest_framework/filters.py b/rest_framework/filters.py
index bcc87660..f7b5a1bc 100644
--- a/rest_framework/filters.py
+++ b/rest_framework/filters.py
@@ -54,6 +54,6 @@ class DjangoFilterBackend(BaseFilterBackend):
filter_class = self.get_filter_class(view)
if filter_class:
- return filter_class(request.GET, queryset=queryset)
+ return filter_class(request.QUERY_PARAMS, queryset=queryset)
return queryset
diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py
index 43581ae9..acaf8a71 100644
--- a/rest_framework/mixins.py
+++ b/rest_framework/mixins.py
@@ -4,6 +4,8 @@ Basic building blocks for generic class based views.
We don't bind behaviour to http method handlers yet,
which allows mixin classes to be composed in interesting ways.
"""
+from __future__ import unicode_literals
+
from django.http import Http404
from rest_framework import status
from rest_framework.response import Response
@@ -41,7 +43,7 @@ class ListModelMixin(object):
List a queryset.
Should be mixed in with `MultipleObjectAPIView`.
"""
- empty_error = u"Empty list and '%(class_name)s.allow_empty' is False."
+ empty_error = "Empty list and '%(class_name)s.allow_empty' is False."
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
diff --git a/rest_framework/negotiation.py b/rest_framework/negotiation.py
index ee2800a6..0a7b6db6 100644
--- a/rest_framework/negotiation.py
+++ b/rest_framework/negotiation.py
@@ -33,7 +33,7 @@ class DefaultContentNegotiation(BaseContentNegotiation):
"""
# Allow URL style format override. eg. "?format=json
format_query_param = self.settings.URL_FORMAT_OVERRIDE
- format = format_suffix or request.GET.get(format_query_param)
+ format = format_suffix or request.QUERY_PARAMS.get(format_query_param)
if format:
renderers = self.filter_renderers(renderers, format)
@@ -80,5 +80,5 @@ class DefaultContentNegotiation(BaseContentNegotiation):
Allows URL style accept override. eg. "?accept=application/json"
"""
header = request.META.get('HTTP_ACCEPT', '*/*')
- header = request.GET.get(self.settings.URL_ACCEPT_OVERRIDE, header)
+ header = request.QUERY_PARAMS.get(self.settings.URL_ACCEPT_OVERRIDE, header)
return [token.strip() for token in header.split(',')]
diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py
index 149d6431..b601156b 100644
--- a/rest_framework/parsers.py
+++ b/rest_framework/parsers.py
@@ -10,6 +10,7 @@ from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser
from django.http.multipartparser import MultiPartParserError
from rest_framework.compat import yaml, ETParseError
from rest_framework.exceptions import ParseError
+from rest_framework.compat import six
from xml.etree import ElementTree as ET
from xml.parsers.expat import ExpatError
import json
@@ -55,9 +56,10 @@ class JSONParser(BaseParser):
`files` will always be `None`.
"""
try:
- return json.load(stream)
- except ValueError, exc:
- raise ParseError('JSON parse error - %s' % unicode(exc))
+ data = stream.read().decode('iso-8859-1')
+ return json.loads(data)
+ except ValueError as exc:
+ raise ParseError('JSON parse error - %s' % six.text_type(exc))
class YAMLParser(BaseParser):
@@ -75,9 +77,10 @@ class YAMLParser(BaseParser):
`files` will always be `None`.
"""
try:
- return yaml.safe_load(stream)
- except (ValueError, yaml.parser.ParserError), exc:
- raise ParseError('YAML parse error - %s' % unicode(exc))
+ data = stream.read().decode('iso-8859-1')
+ return yaml.safe_load(data)
+ except (ValueError, yaml.parser.ParserError) as exc:
+ raise ParseError('YAML parse error - %s' % six.u(exc))
class FormParser(BaseParser):
@@ -121,8 +124,8 @@ class MultiPartParser(BaseParser):
parser = DjangoMultiPartParser(meta, stream, upload_handlers)
data, files = parser.parse()
return DataAndFiles(data, files)
- except MultiPartParserError, exc:
- raise ParseError('Multipart form parse error - %s' % unicode(exc))
+ except MultiPartParserError as exc:
+ raise ParseError('Multipart form parse error - %s' % six.u(exc))
class XMLParser(BaseParser):
@@ -135,8 +138,8 @@ class XMLParser(BaseParser):
def parse(self, stream, media_type=None, parser_context=None):
try:
tree = ET.parse(stream)
- except (ExpatError, ETParseError, ValueError), exc:
- raise ParseError('XML parse error - %s' % unicode(exc))
+ except (ExpatError, ETParseError, ValueError) as exc:
+ raise ParseError('XML parse error - %s' % six.u(exc))
data = self._xml_convert(tree.getroot())
return data
@@ -146,7 +149,7 @@ class XMLParser(BaseParser):
convert the xml `element` into the corresponding python object
"""
- children = element.getchildren()
+ children = list(element)
if len(children) == 0:
return self._type_convert(element.text)
diff --git a/rest_framework/relations.py b/rest_framework/relations.py
index d49ca39b..93f19362 100644
--- a/rest_framework/relations.py
+++ b/rest_framework/relations.py
@@ -1,15 +1,20 @@
+
+from __future__ import unicode_literals
+
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.core.urlresolvers import resolve, get_script_prefix
from django import forms
from django.forms import widgets
from django.forms.models import ModelChoiceIterator
-from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _
from rest_framework.fields import Field, WritableField
from rest_framework.reverse import reverse
from urlparse import urlparse
+from rest_framework.compat import urlparse
+from rest_framework.compat import smart_text
import warnings
+
##### Relational fields #####
@@ -72,8 +77,8 @@ class RelatedField(WritableField):
"""
Return a readable representation for use with eg. select widgets.
"""
- desc = smart_unicode(obj)
- ident = smart_unicode(self.to_native(obj))
+ desc = smart_text(obj)
+ ident = smart_text(self.to_native(obj))
if desc == ident:
return desc
return "%s - %s" % (desc, ident)
@@ -177,8 +182,8 @@ class PrimaryKeyRelatedField(RelatedField):
"""
Return a readable representation for use with eg. select widgets.
"""
- desc = smart_unicode(obj)
- ident = smart_unicode(self.to_native(obj.pk))
+ desc = smart_text(obj)
+ ident = smart_text(self.to_native(obj.pk))
if desc == ident:
return desc
return "%s - %s" % (desc, ident)
@@ -194,7 +199,7 @@ class PrimaryKeyRelatedField(RelatedField):
try:
return self.queryset.get(pk=data)
except ObjectDoesNotExist:
- msg = self.error_messages['does_not_exist'] % smart_unicode(data)
+ msg = self.error_messages['does_not_exist'] % smart_text(data)
raise ValidationError(msg)
except (TypeError, ValueError):
received = type(data).__name__
@@ -224,6 +229,7 @@ class PrimaryKeyRelatedField(RelatedField):
pk = getattr(obj, self.source or field_name).pk
except ObjectDoesNotExist:
return None
+ return self.to_native(obj.pk)
# Forward relationship
return self.to_native(pk)
@@ -259,7 +265,7 @@ class SlugRelatedField(RelatedField):
return self.queryset.get(**{self.slug_field: data})
except ObjectDoesNotExist:
raise ValidationError(self.error_messages['does_not_exist'] %
- (self.slug_field, unicode(data)))
+ (self.slug_field, smart_text(data)))
except (TypeError, ValueError):
msg = self.error_messages['invalid']
raise ValidationError(msg)
@@ -350,7 +356,7 @@ class HyperlinkedRelatedField(RelatedField):
if http_prefix:
# If needed convert absolute URLs to relative path
- value = urlparse(value).path
+ value = urlparse.urlparse(value).path
prefix = get_script_prefix()
if value.startswith(prefix):
value = '/' + value[len(prefix):]
diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py
index ed11551b..74c7e2c9 100644
--- a/rest_framework/renderers.py
+++ b/rest_framework/renderers.py
@@ -6,6 +6,8 @@ on the response, such as JSON encoded data or HTML output.
REST framework also provides an HTML renderer the renders the browsable API.
"""
+from __future__ import unicode_literals
+
import copy
import string
import json
@@ -60,7 +62,7 @@ class JSONRenderer(BaseRenderer):
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)
+ base_media_type, params = parse_header(accepted_media_type.encode('ascii'))
indent = params.get('indent', indent)
try:
indent = max(min(int(indent), 8), 0)
@@ -86,7 +88,7 @@ class JSONPRenderer(JSONRenderer):
Determine the name of the callback to wrap around the json output.
"""
request = renderer_context.get('request', None)
- params = request and request.GET or {}
+ params = request and request.QUERY_PARAMS or {}
return params.get(self.callback_parameter, self.default_callback)
def render(self, data, accepted_media_type=None, renderer_context=None):
@@ -100,7 +102,7 @@ class JSONPRenderer(JSONRenderer):
callback = self.get_callback(renderer_context)
json = super(JSONPRenderer, self).render(data, accepted_media_type,
renderer_context)
- return u"%s(%s);" % (callback, json)
+ return "%s(%s);" % (callback, json)
class XMLRenderer(BaseRenderer):
@@ -358,7 +360,7 @@ class BrowsableAPIRenderer(BaseRenderer):
# Creating an on the fly form see:
# http://stackoverflow.com/questions/3915024/dynamically-creating-classes-python
- OnTheFlyForm = type("OnTheFlyForm", (forms.Form,), fields)
+ OnTheFlyForm = type(str("OnTheFlyForm"), (forms.Form,), fields)
data = (obj is not None) and serializer.data or None
form_instance = OnTheFlyForm(data)
return form_instance
diff --git a/rest_framework/request.py b/rest_framework/request.py
index 1c28cd17..597892ef 100644
--- a/rest_framework/request.py
+++ b/rest_framework/request.py
@@ -9,10 +9,11 @@ The wrapped request then offers a richer API, in particular :
- full support of PUT method, including support for file uploads
- form overloading of HTTP method, content type and content
"""
-from StringIO import StringIO
from django.http.multipartparser import parse_header
+from rest_framework import HTTP_HEADER_ENCODING
from rest_framework import exceptions
+from rest_framework.compat import BytesIO
from rest_framework.settings import api_settings
@@ -20,7 +21,7 @@ def is_form_media_type(media_type):
"""
Return True if the media type is a valid form media type.
"""
- base_media_type, params = parse_header(media_type)
+ base_media_type, params = parse_header(media_type.encode(HTTP_HEADER_ENCODING))
return (base_media_type == 'application/x-www-form-urlencoded' or
base_media_type == 'multipart/form-data')
@@ -242,7 +243,7 @@ class Request(object):
elif hasattr(self._request, 'read'):
self._stream = self._request
else:
- self._stream = StringIO(self.raw_post_data)
+ self._stream = BytesIO(self.raw_post_data)
def _perform_form_overloading(self):
"""
@@ -277,7 +278,7 @@ class Request(object):
self._CONTENT_PARAM in self._data and
self._CONTENTTYPE_PARAM in self._data):
self._content_type = self._data[self._CONTENTTYPE_PARAM]
- self._stream = StringIO(self._data[self._CONTENT_PARAM])
+ self._stream = BytesIO(self._data[self._CONTENT_PARAM].encode(HTTP_HEADER_ENCODING))
self._data, self._files = (Empty, Empty)
def _parse(self):
diff --git a/rest_framework/response.py b/rest_framework/response.py
index be78c43a..0a484c4a 100644
--- a/rest_framework/response.py
+++ b/rest_framework/response.py
@@ -1,6 +1,8 @@
from django.core.handlers.wsgi import STATUS_CODE_TEXT
from django.template.response import SimpleTemplateResponse
+from rest_framework.compat import six
+
class Response(SimpleTemplateResponse):
"""
@@ -22,9 +24,9 @@ class Response(SimpleTemplateResponse):
self.data = data
self.template_name = template_name
self.exception = exception
-
+
if headers:
- for name,value in headers.iteritems():
+ for name, value in six.iteritems(headers):
self[name] = value
@property
diff --git a/rest_framework/runtests/runtests.py b/rest_framework/runtests/runtests.py
index 505994e2..4a333fb3 100755
--- a/rest_framework/runtests/runtests.py
+++ b/rest_framework/runtests/runtests.py
@@ -33,7 +33,7 @@ def main():
elif len(sys.argv) == 1:
test_case = ''
else:
- print usage()
+ print(usage())
sys.exit(1)
failures = test_runner.run_tests(['tests' + test_case])
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 6b2a8368..c5b3494c 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -7,6 +7,7 @@ from django.db import models
from django.forms import widgets
from django.utils.datastructures import SortedDict
from rest_framework.compat import get_concrete_model
+from rest_framework.compat import six
# Note: We do the following so that users of the framework can use this style:
#
@@ -64,7 +65,7 @@ def _get_declared_fields(bases, attrs):
Note that all fields from the base classes are used.
"""
fields = [(field_name, attrs.pop(field_name))
- for field_name, obj in attrs.items()
+ for field_name, obj in list(six.iteritems(attrs))
if isinstance(obj, Field)]
fields.sort(key=lambda x: x[1].creation_counter)
@@ -73,7 +74,7 @@ def _get_declared_fields(bases, attrs):
# in order to maintain the correct order of fields.
for base in bases[::-1]:
if hasattr(base, 'base_fields'):
- fields = base.base_fields.items() + fields
+ fields = list(base.base_fields.items()) + fields
return SortedDict(fields)
@@ -200,7 +201,7 @@ class BaseSerializer(Field):
reverted_data = {}
if data is not None and not isinstance(data, dict):
- self._errors['non_field_errors'] = [u'Invalid data']
+ self._errors['non_field_errors'] = ['Invalid data']
return None
for field_name, field in self.fields.items():
@@ -278,6 +279,10 @@ class BaseSerializer(Field):
"""
Deserialize primitives -> objects.
"""
+ if hasattr(data, '__iter__') and not isinstance(data, (dict, six.text_type)):
+ # TODO: error data when deserializing lists
+ return [self.from_native(item, None) for item in data]
+
self._errors = {}
if data is not None or files is not None:
attrs = self.restore_fields(data, files)
@@ -379,8 +384,8 @@ class BaseSerializer(Field):
return self.object
-class Serializer(BaseSerializer):
- __metaclass__ = SerializerMetaclass
+class Serializer(six.with_metaclass(SerializerMetaclass, BaseSerializer)):
+ pass
class ModelSerializerOptions(SerializerOptions):
@@ -544,7 +549,7 @@ class ModelSerializer(Serializer):
"""
try:
instance.full_clean(exclude=self.get_validation_exclusions())
- except ValidationError, err:
+ except ValidationError as err:
self._errors = err.message_dict
return None
return instance
@@ -580,6 +585,12 @@ class ModelSerializer(Serializer):
else:
instance = self.opts.model(**attrs)
+ try:
+ instance.full_clean(exclude=self.get_validation_exclusions())
+ except ValidationError as err:
+ self._errors = err.message_dict
+ return None
+
return instance
def from_native(self, data, files):
diff --git a/rest_framework/settings.py b/rest_framework/settings.py
index 5c77c55c..b3ca0134 100644
--- a/rest_framework/settings.py
+++ b/rest_framework/settings.py
@@ -19,6 +19,7 @@ back to the defaults.
"""
from django.conf import settings
from django.utils import importlib
+from rest_framework.compat import six
USER_SETTINGS = getattr(settings, 'REST_FRAMEWORK', None)
@@ -98,7 +99,7 @@ def perform_import(val, setting_name):
If the given setting is a string import notation,
then perform the necessary import or imports.
"""
- if isinstance(val, basestring):
+ if isinstance(val, six.string_types):
return import_from_string(val, setting_name)
elif isinstance(val, (list, tuple)):
return [import_from_string(item, setting_name) for item in val]
diff --git a/rest_framework/six.py b/rest_framework/six.py
new file mode 100644
index 00000000..9e382312
--- /dev/null
+++ b/rest_framework/six.py
@@ -0,0 +1,389 @@
+"""Utilities for writing code that runs on Python 2 and 3"""
+
+import operator
+import sys
+import types
+
+__author__ = "Benjamin Peterson <benjamin@python.org>"
+__version__ = "1.2.0"
+
+
+# True if we are running on Python 3.
+PY3 = sys.version_info[0] == 3
+
+if PY3:
+ string_types = str,
+ integer_types = int,
+ class_types = type,
+ text_type = str
+ binary_type = bytes
+
+ MAXSIZE = sys.maxsize
+else:
+ string_types = basestring,
+ integer_types = (int, long)
+ class_types = (type, types.ClassType)
+ text_type = unicode
+ binary_type = str
+
+ if sys.platform == "java":
+ # Jython always uses 32 bits.
+ MAXSIZE = int((1 << 31) - 1)
+ else:
+ # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
+ class X(object):
+ def __len__(self):
+ return 1 << 31
+ try:
+ len(X())
+ except OverflowError:
+ # 32-bit
+ MAXSIZE = int((1 << 31) - 1)
+ else:
+ # 64-bit
+ MAXSIZE = int((1 << 63) - 1)
+ del X
+
+
+def _add_doc(func, doc):
+ """Add documentation to a function."""
+ func.__doc__ = doc
+
+
+def _import_module(name):
+ """Import module, returning the module after the last dot."""
+ __import__(name)
+ return sys.modules[name]
+
+
+class _LazyDescr(object):
+
+ def __init__(self, name):
+ self.name = name
+
+ def __get__(self, obj, tp):
+ result = self._resolve()
+ setattr(obj, self.name, result)
+ # This is a bit ugly, but it avoids running this again.
+ delattr(tp, self.name)
+ return result
+
+
+class MovedModule(_LazyDescr):
+
+ def __init__(self, name, old, new=None):
+ super(MovedModule, self).__init__(name)
+ if PY3:
+ if new is None:
+ new = name
+ self.mod = new
+ else:
+ self.mod = old
+
+ def _resolve(self):
+ return _import_module(self.mod)
+
+
+class MovedAttribute(_LazyDescr):
+
+ def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
+ super(MovedAttribute, self).__init__(name)
+ if PY3:
+ if new_mod is None:
+ new_mod = name
+ self.mod = new_mod
+ if new_attr is None:
+ if old_attr is None:
+ new_attr = name
+ else:
+ new_attr = old_attr
+ self.attr = new_attr
+ else:
+ self.mod = old_mod
+ if old_attr is None:
+ old_attr = name
+ self.attr = old_attr
+
+ def _resolve(self):
+ module = _import_module(self.mod)
+ return getattr(module, self.attr)
+
+
+
+class _MovedItems(types.ModuleType):
+ """Lazy loading of moved objects"""
+
+
+_moved_attributes = [
+ MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
+ MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
+ MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
+ MovedAttribute("map", "itertools", "builtins", "imap", "map"),
+ MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
+ MovedAttribute("reduce", "__builtin__", "functools"),
+ MovedAttribute("StringIO", "StringIO", "io"),
+ MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
+ MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
+
+ MovedModule("builtins", "__builtin__"),
+ MovedModule("configparser", "ConfigParser"),
+ MovedModule("copyreg", "copy_reg"),
+ MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
+ MovedModule("http_cookies", "Cookie", "http.cookies"),
+ MovedModule("html_entities", "htmlentitydefs", "html.entities"),
+ MovedModule("html_parser", "HTMLParser", "html.parser"),
+ MovedModule("http_client", "httplib", "http.client"),
+ MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
+ MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
+ MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
+ MovedModule("cPickle", "cPickle", "pickle"),
+ MovedModule("queue", "Queue"),
+ MovedModule("reprlib", "repr"),
+ MovedModule("socketserver", "SocketServer"),
+ MovedModule("tkinter", "Tkinter"),
+ MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
+ MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
+ MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
+ MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
+ MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
+ MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
+ MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
+ MovedModule("tkinter_colorchooser", "tkColorChooser",
+ "tkinter.colorchooser"),
+ MovedModule("tkinter_commondialog", "tkCommonDialog",
+ "tkinter.commondialog"),
+ MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
+ MovedModule("tkinter_font", "tkFont", "tkinter.font"),
+ MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
+ MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
+ "tkinter.simpledialog"),
+ MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
+ MovedModule("winreg", "_winreg"),
+]
+for attr in _moved_attributes:
+ setattr(_MovedItems, attr.name, attr)
+del attr
+
+moves = sys.modules["django.utils.six.moves"] = _MovedItems("moves")
+
+
+def add_move(move):
+ """Add an item to six.moves."""
+ setattr(_MovedItems, move.name, move)
+
+
+def remove_move(name):
+ """Remove item from six.moves."""
+ try:
+ delattr(_MovedItems, name)
+ except AttributeError:
+ try:
+ del moves.__dict__[name]
+ except KeyError:
+ raise AttributeError("no such move, %r" % (name,))
+
+
+if PY3:
+ _meth_func = "__func__"
+ _meth_self = "__self__"
+
+ _func_code = "__code__"
+ _func_defaults = "__defaults__"
+
+ _iterkeys = "keys"
+ _itervalues = "values"
+ _iteritems = "items"
+else:
+ _meth_func = "im_func"
+ _meth_self = "im_self"
+
+ _func_code = "func_code"
+ _func_defaults = "func_defaults"
+
+ _iterkeys = "iterkeys"
+ _itervalues = "itervalues"
+ _iteritems = "iteritems"
+
+
+try:
+ advance_iterator = next
+except NameError:
+ def advance_iterator(it):
+ return it.next()
+next = advance_iterator
+
+
+if PY3:
+ def get_unbound_function(unbound):
+ return unbound
+
+ Iterator = object
+
+ def callable(obj):
+ return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
+else:
+ def get_unbound_function(unbound):
+ return unbound.im_func
+
+ class Iterator(object):
+
+ def next(self):
+ return type(self).__next__(self)
+
+ callable = callable
+_add_doc(get_unbound_function,
+ """Get the function out of a possibly unbound function""")
+
+
+get_method_function = operator.attrgetter(_meth_func)
+get_method_self = operator.attrgetter(_meth_self)
+get_function_code = operator.attrgetter(_func_code)
+get_function_defaults = operator.attrgetter(_func_defaults)
+
+
+def iterkeys(d):
+ """Return an iterator over the keys of a dictionary."""
+ return iter(getattr(d, _iterkeys)())
+
+def itervalues(d):
+ """Return an iterator over the values of a dictionary."""
+ return iter(getattr(d, _itervalues)())
+
+def iteritems(d):
+ """Return an iterator over the (key, value) pairs of a dictionary."""
+ return iter(getattr(d, _iteritems)())
+
+
+if PY3:
+ def b(s):
+ return s.encode("latin-1")
+ def u(s):
+ return s
+ if sys.version_info[1] <= 1:
+ def int2byte(i):
+ return bytes((i,))
+ else:
+ # This is about 2x faster than the implementation above on 3.2+
+ int2byte = operator.methodcaller("to_bytes", 1, "big")
+ import io
+ StringIO = io.StringIO
+ BytesIO = io.BytesIO
+else:
+ def b(s):
+ return s
+ def u(s):
+ return unicode(s, "unicode_escape")
+ int2byte = chr
+ import StringIO
+ StringIO = BytesIO = StringIO.StringIO
+_add_doc(b, """Byte literal""")
+_add_doc(u, """Text literal""")
+
+
+if PY3:
+ import builtins
+ exec_ = getattr(builtins, "exec")
+
+
+ def reraise(tp, value, tb=None):
+ if value.__traceback__ is not tb:
+ raise value.with_traceback(tb)
+ raise value
+
+
+ print_ = getattr(builtins, "print")
+ del builtins
+
+else:
+ def exec_(code, globs=None, locs=None):
+ """Execute code in a namespace."""
+ if globs is None:
+ frame = sys._getframe(1)
+ globs = frame.f_globals
+ if locs is None:
+ locs = frame.f_locals
+ del frame
+ elif locs is None:
+ locs = globs
+ exec("""exec code in globs, locs""")
+
+
+ exec_("""def reraise(tp, value, tb=None):
+ raise tp, value, tb
+""")
+
+
+ def print_(*args, **kwargs):
+ """The new-style print function."""
+ fp = kwargs.pop("file", sys.stdout)
+ if fp is None:
+ return
+ def write(data):
+ if not isinstance(data, basestring):
+ data = str(data)
+ fp.write(data)
+ want_unicode = False
+ sep = kwargs.pop("sep", None)
+ if sep is not None:
+ if isinstance(sep, unicode):
+ want_unicode = True
+ elif not isinstance(sep, str):
+ raise TypeError("sep must be None or a string")
+ end = kwargs.pop("end", None)
+ if end is not None:
+ if isinstance(end, unicode):
+ want_unicode = True
+ elif not isinstance(end, str):
+ raise TypeError("end must be None or a string")
+ if kwargs:
+ raise TypeError("invalid keyword arguments to print()")
+ if not want_unicode:
+ for arg in args:
+ if isinstance(arg, unicode):
+ want_unicode = True
+ break
+ if want_unicode:
+ newline = unicode("\n")
+ space = unicode(" ")
+ else:
+ newline = "\n"
+ space = " "
+ if sep is None:
+ sep = space
+ if end is None:
+ end = newline
+ for i, arg in enumerate(args):
+ if i:
+ write(sep)
+ write(arg)
+ write(end)
+
+_add_doc(reraise, """Reraise an exception.""")
+
+
+def with_metaclass(meta, base=object):
+ """Create a base class with a metaclass."""
+ return meta("NewBase", (base,), {})
+
+
+### Additional customizations for Django ###
+
+if PY3:
+ _iterlists = "lists"
+ _assertRaisesRegex = "assertRaisesRegex"
+else:
+ _iterlists = "iterlists"
+ _assertRaisesRegex = "assertRaisesRegexp"
+
+
+def iterlists(d):
+ """Return an iterator over the values of a MultiValueDict."""
+ return getattr(d, _iterlists)()
+
+
+def assertRaisesRegex(self, *args, **kwargs):
+ return getattr(self, _assertRaisesRegex)(*args, **kwargs)
+
+
+add_move(MovedModule("_dummy_thread", "dummy_thread"))
+add_move(MovedModule("_thread", "thread"))
diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py
index 82fcdfe7..cbafbe0e 100644
--- a/rest_framework/templatetags/rest_framework.py
+++ b/rest_framework/templatetags/rest_framework.py
@@ -1,10 +1,14 @@
+from __future__ import unicode_literals, absolute_import
+
from django import template
from django.core.urlresolvers import reverse
from django.http import QueryDict
-from django.utils.encoding import force_unicode
from django.utils.html import escape
from django.utils.safestring import SafeData, mark_safe
-from urlparse import urlsplit, urlunsplit
+from rest_framework.compat import urlparse
+from rest_framework.compat import force_text
+from rest_framework.compat import six
+
import re
import string
@@ -99,11 +103,11 @@ def replace_query_param(url, key, val):
Given a URL and a key/val pair, set or replace an item in the query
parameters of the URL, and return the new URL.
"""
- (scheme, netloc, path, query, fragment) = urlsplit(url)
+ (scheme, netloc, path, query, fragment) = urlparse.urlsplit(url)
query_dict = QueryDict(query).copy()
query_dict[key] = val
query = query_dict.urlencode()
- return urlunsplit((scheme, netloc, path, query, fragment))
+ return urlparse.urlunsplit((scheme, netloc, path, query, fragment))
# Regex for adding classes to html snippets
@@ -179,7 +183,7 @@ def add_class(value, css_class):
In the case of REST Framework, the filter is used to add Bootstrap-specific
classes to the forms.
"""
- html = unicode(value)
+ html = six.text_type(value)
match = class_re.search(html)
if match:
m = re.search(r'^%s$|^%s\s|\s%s\s|\s%s$' % (css_class, css_class,
@@ -213,7 +217,7 @@ def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=Tru
"""
trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x
safe_input = isinstance(text, SafeData)
- words = word_split_re.split(force_unicode(text))
+ words = word_split_re.split(force_text(text))
nofollow_attr = nofollow and ' rel="nofollow"' or ''
for i, word in enumerate(words):
match = None
@@ -249,4 +253,4 @@ def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=Tru
words[i] = mark_safe(word)
elif autoescape:
words[i] = escape(word)
- return mark_safe(u''.join(words))
+ return mark_safe(''.join(words))
diff --git a/rest_framework/tests/authentication.py b/rest_framework/tests/authentication.py
index 1f17e8d2..7dde6d22 100644
--- a/rest_framework/tests/authentication.py
+++ b/rest_framework/tests/authentication.py
@@ -1,7 +1,9 @@
+from __future__ import unicode_literals
+
from django.contrib.auth.models import User
from django.http import HttpResponse
from django.test import Client, TestCase
-
+from rest_framework import HTTP_HEADER_ENCODING
from rest_framework import permissions
from rest_framework.authtoken.models import Token
from rest_framework.authentication import TokenAuthentication, BasicAuthentication, SessionAuthentication
@@ -42,13 +44,17 @@ class BasicAuthTests(TestCase):
def test_post_form_passing_basic_auth(self):
"""Ensure POSTing json over basic auth with correct credentials passes and does not require CSRF"""
- auth = 'Basic %s' % base64.encodestring('%s:%s' % (self.username, self.password)).strip()
+ credentials = ('%s:%s' % (self.username, self.password))
+ base64_credentials = base64.b64encode(credentials.encode(HTTP_HEADER_ENCODING)).decode(HTTP_HEADER_ENCODING)
+ auth = 'Basic %s' % base64_credentials
response = self.csrf_client.post('/basic/', {'example': 'example'}, HTTP_AUTHORIZATION=auth)
self.assertEqual(response.status_code, 200)
def test_post_json_passing_basic_auth(self):
"""Ensure POSTing form over basic auth with correct credentials passes and does not require CSRF"""
- auth = 'Basic %s' % base64.encodestring('%s:%s' % (self.username, self.password)).strip()
+ credentials = ('%s:%s' % (self.username, self.password))
+ base64_credentials = base64.b64encode(credentials.encode(HTTP_HEADER_ENCODING)).decode(HTTP_HEADER_ENCODING)
+ auth = 'Basic %s' % base64_credentials
response = self.csrf_client.post('/basic/', json.dumps({'example': 'example'}), 'application/json', HTTP_AUTHORIZATION=auth)
self.assertEqual(response.status_code, 200)
@@ -159,7 +165,7 @@ class TokenAuthTests(TestCase):
response = client.post('/auth-token/',
json.dumps({'username': self.username, 'password': self.password}), 'application/json')
self.assertEqual(response.status_code, 200)
- self.assertEqual(json.loads(response.content)['token'], self.key)
+ self.assertEqual(json.loads(response.content.decode('ascii'))['token'], self.key)
def test_token_login_json_bad_creds(self):
"""Ensure token login view using JSON POST fails if bad credentials are used."""
@@ -181,4 +187,4 @@ class TokenAuthTests(TestCase):
response = client.post('/auth-token/',
{'username': self.username, 'password': self.password})
self.assertEqual(response.status_code, 200)
- self.assertEqual(json.loads(response.content)['token'], self.key)
+ self.assertEqual(json.loads(response.content.decode('ascii'))['token'], self.key)
diff --git a/rest_framework/tests/files.py b/rest_framework/tests/files.py
index 446e23c0..0434f900 100644
--- a/rest_framework/tests/files.py
+++ b/rest_framework/tests/files.py
@@ -1,9 +1,10 @@
-import StringIO
import datetime
from django.test import TestCase
from rest_framework import serializers
+from rest_framework.compat import BytesIO
+from rest_framework.compat import six
class UploadedFile(object):
@@ -27,9 +28,9 @@ class UploadedFileSerializer(serializers.Serializer):
class FileSerializerTests(TestCase):
def test_create(self):
now = datetime.datetime.now()
- file = StringIO.StringIO('stuff')
+ file = BytesIO(six.b('stuff'))
file.name = 'stuff.txt'
- file.size = file.len
+ file.size = len(file.getvalue())
serializer = UploadedFileSerializer(data={'created': now}, files={'file': file})
uploaded_file = UploadedFile(file=file, created=now)
self.assertTrue(serializer.is_valid())
diff --git a/rest_framework/tests/genericrelations.py b/rest_framework/tests/genericrelations.py
index 146ad1e4..91a98604 100644
--- a/rest_framework/tests/genericrelations.py
+++ b/rest_framework/tests/genericrelations.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.generic import GenericRelation, GenericForeignKey
from django.db import models
@@ -63,8 +65,8 @@ class TestGenericRelations(TestCase):
serializer = BookmarkSerializer(self.bookmark)
expected = {
- 'tags': [u'django', u'python'],
- 'url': u'https://www.djangoproject.com/'
+ 'tags': ['django', 'python'],
+ 'url': 'https://www.djangoproject.com/'
}
self.assertEquals(serializer.data, expected)
@@ -84,16 +86,16 @@ class TestGenericRelations(TestCase):
serializer = TagSerializer(Tag.objects.all())
expected = [
{
- 'tag': u'django',
- 'tagged_item': u'Bookmark: https://www.djangoproject.com/'
+ 'tag': 'django',
+ 'tagged_item': 'Bookmark: https://www.djangoproject.com/'
},
{
- 'tag': u'python',
- 'tagged_item': u'Bookmark: https://www.djangoproject.com/'
+ 'tag': 'python',
+ 'tagged_item': 'Bookmark: https://www.djangoproject.com/'
},
{
- 'tag': u'reminder',
- 'tagged_item': u'Note: Remember the milk'
+ 'tag': 'reminder',
+ 'tagged_item': 'Note: Remember the milk'
}
]
self.assertEquals(serializer.data, expected)
diff --git a/rest_framework/tests/generics.py b/rest_framework/tests/generics.py
index 4799a04b..fd01312a 100644
--- a/rest_framework/tests/generics.py
+++ b/rest_framework/tests/generics.py
@@ -1,10 +1,11 @@
-import json
+from __future__ import unicode_literals
from django.db import models
from django.test import TestCase
from rest_framework import generics, serializers, status
from rest_framework.tests.utils import RequestFactory
from rest_framework.tests.models import BasicModel, Comment, SlugBasedModel
-
+from rest_framework.compat import six
+import json
factory = RequestFactory()
@@ -72,7 +73,7 @@ class TestRootView(TestCase):
content_type='application/json')
response = self.view(request).render()
self.assertEquals(response.status_code, status.HTTP_201_CREATED)
- self.assertEquals(response.data, {'id': 4, 'text': u'foobar'})
+ self.assertEquals(response.data, {'id': 4, 'text': 'foobar'})
created = self.objects.get(id=4)
self.assertEquals(created.text, 'foobar')
@@ -127,7 +128,7 @@ class TestRootView(TestCase):
content_type='application/json')
response = self.view(request).render()
self.assertEquals(response.status_code, status.HTTP_201_CREATED)
- self.assertEquals(response.data, {'id': 4, 'text': u'foobar'})
+ self.assertEquals(response.data, {'id': 4, 'text': 'foobar'})
created = self.objects.get(id=4)
self.assertEquals(created.text, 'foobar')
@@ -202,7 +203,7 @@ class TestInstanceView(TestCase):
request = factory.delete('/1')
response = self.view(request, pk=1).render()
self.assertEquals(response.status_code, status.HTTP_204_NO_CONTENT)
- self.assertEquals(response.content, '')
+ self.assertEquals(response.content, six.b(''))
ids = [obj.id for obj in self.objects.all()]
self.assertEquals(ids, [2, 3])
diff --git a/rest_framework/tests/htmlrenderer.py b/rest_framework/tests/htmlrenderer.py
index 54096206..34caa208 100644
--- a/rest_framework/tests/htmlrenderer.py
+++ b/rest_framework/tests/htmlrenderer.py
@@ -7,6 +7,7 @@ from rest_framework.compat import patterns, url
from rest_framework.decorators import api_view, renderer_classes
from rest_framework.renderers import TemplateHTMLRenderer
from rest_framework.response import Response
+from rest_framework.compat import six
@api_view(('GET',))
@@ -68,13 +69,13 @@ class TemplateHTMLRendererTests(TestCase):
def test_not_found_html_view(self):
response = self.client.get('/not_found')
self.assertEquals(response.status_code, 404)
- self.assertEquals(response.content, "404 Not Found")
+ self.assertEquals(response.content, six.b("404 Not Found"))
self.assertEquals(response['Content-Type'], 'text/html')
def test_permission_denied_html_view(self):
response = self.client.get('/permission_denied')
self.assertEquals(response.status_code, 403)
- self.assertEquals(response.content, "403 Forbidden")
+ self.assertEquals(response.content, six.b("403 Forbidden"))
self.assertEquals(response['Content-Type'], 'text/html')
@@ -105,11 +106,11 @@ class TemplateHTMLRendererExceptionTests(TestCase):
def test_not_found_html_view_with_template(self):
response = self.client.get('/not_found')
self.assertEquals(response.status_code, 404)
- self.assertEquals(response.content, "404: Not found")
+ self.assertEquals(response.content, six.b("404: Not found"))
self.assertEquals(response['Content-Type'], 'text/html')
def test_permission_denied_html_view_with_template(self):
response = self.client.get('/permission_denied')
self.assertEquals(response.status_code, 403)
- self.assertEquals(response.content, "403: Permission denied")
+ self.assertEquals(response.content, six.b("403: Permission denied"))
self.assertEquals(response['Content-Type'], 'text/html')
diff --git a/rest_framework/tests/negotiation.py b/rest_framework/tests/negotiation.py
index e06354ea..7706908b 100644
--- a/rest_framework/tests/negotiation.py
+++ b/rest_framework/tests/negotiation.py
@@ -1,6 +1,8 @@
from django.test import TestCase
from django.test.client import RequestFactory
from rest_framework.negotiation import DefaultContentNegotiation
+from rest_framework.request import Request
+
factory = RequestFactory()
@@ -22,16 +24,16 @@ class TestAcceptedMediaType(TestCase):
return self.negotiator.select_renderer(request, self.renderers)
def test_client_without_accept_use_renderer(self):
- request = factory.get('/')
+ request = Request(factory.get('/'))
accepted_renderer, accepted_media_type = self.select_renderer(request)
self.assertEquals(accepted_media_type, 'application/json')
def test_client_underspecifies_accept_use_renderer(self):
- request = factory.get('/', HTTP_ACCEPT='*/*')
+ request = Request(factory.get('/', HTTP_ACCEPT='*/*'))
accepted_renderer, accepted_media_type = self.select_renderer(request)
self.assertEquals(accepted_media_type, 'application/json')
def test_client_overspecifies_accept_use_client(self):
- request = factory.get('/', HTTP_ACCEPT='application/json; indent=8')
+ request = Request(factory.get('/', HTTP_ACCEPT='application/json; indent=8'))
accepted_renderer, accepted_media_type = self.select_renderer(request)
self.assertEquals(accepted_media_type, 'application/json; indent=8')
diff --git a/rest_framework/tests/parsers.py b/rest_framework/tests/parsers.py
index 8ab8a52f..ffa39b1f 100644
--- a/rest_framework/tests/parsers.py
+++ b/rest_framework/tests/parsers.py
@@ -131,7 +131,7 @@
# self.assertEqual(data['key1'], 'val1')
# self.assertEqual(files['file1'].read(), 'blablabla')
-from StringIO import StringIO
+from rest_framework.compat import StringIO
from django import forms
from django.test import TestCase
from rest_framework.parsers import FormParser
diff --git a/rest_framework/tests/relations_hyperlink.py b/rest_framework/tests/relations_hyperlink.py
index 7bc36dee..76e31476 100644
--- a/rest_framework/tests/relations_hyperlink.py
+++ b/rest_framework/tests/relations_hyperlink.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
from django.test import TestCase
from rest_framework import serializers
from rest_framework.compat import patterns, url
@@ -74,9 +76,9 @@ class HyperlinkedManyToManyTests(TestCase):
queryset = ManyToManySource.objects.all()
serializer = ManyToManySourceSerializer(queryset)
expected = [
- {'url': '/manytomanysource/1/', 'name': u'source-1', 'targets': ['/manytomanytarget/1/']},
- {'url': '/manytomanysource/2/', 'name': u'source-2', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/']},
- {'url': '/manytomanysource/3/', 'name': u'source-3', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']}
+ {'url': '/manytomanysource/1/', 'name': 'source-1', 'targets': ['/manytomanytarget/1/']},
+ {'url': '/manytomanysource/2/', 'name': 'source-2', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/']},
+ {'url': '/manytomanysource/3/', 'name': 'source-3', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']}
]
self.assertEquals(serializer.data, expected)
@@ -84,14 +86,14 @@ class HyperlinkedManyToManyTests(TestCase):
queryset = ManyToManyTarget.objects.all()
serializer = ManyToManyTargetSerializer(queryset)
expected = [
- {'url': '/manytomanytarget/1/', 'name': u'target-1', 'sources': ['/manytomanysource/1/', '/manytomanysource/2/', '/manytomanysource/3/']},
- {'url': '/manytomanytarget/2/', 'name': u'target-2', 'sources': ['/manytomanysource/2/', '/manytomanysource/3/']},
- {'url': '/manytomanytarget/3/', 'name': u'target-3', 'sources': ['/manytomanysource/3/']}
+ {'url': '/manytomanytarget/1/', 'name': 'target-1', 'sources': ['/manytomanysource/1/', '/manytomanysource/2/', '/manytomanysource/3/']},
+ {'url': '/manytomanytarget/2/', 'name': 'target-2', 'sources': ['/manytomanysource/2/', '/manytomanysource/3/']},
+ {'url': '/manytomanytarget/3/', 'name': 'target-3', 'sources': ['/manytomanysource/3/']}
]
self.assertEquals(serializer.data, expected)
def test_many_to_many_update(self):
- data = {'url': '/manytomanysource/1/', 'name': u'source-1', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']}
+ data = {'url': '/manytomanysource/1/', 'name': 'source-1', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']}
instance = ManyToManySource.objects.get(pk=1)
serializer = ManyToManySourceSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -102,14 +104,14 @@ class HyperlinkedManyToManyTests(TestCase):
queryset = ManyToManySource.objects.all()
serializer = ManyToManySourceSerializer(queryset)
expected = [
- {'url': '/manytomanysource/1/', 'name': u'source-1', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']},
- {'url': '/manytomanysource/2/', 'name': u'source-2', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/']},
- {'url': '/manytomanysource/3/', 'name': u'source-3', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']}
+ {'url': '/manytomanysource/1/', 'name': 'source-1', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']},
+ {'url': '/manytomanysource/2/', 'name': 'source-2', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/']},
+ {'url': '/manytomanysource/3/', 'name': 'source-3', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']}
]
self.assertEquals(serializer.data, expected)
def test_reverse_many_to_many_update(self):
- data = {'url': '/manytomanytarget/1/', 'name': u'target-1', 'sources': ['/manytomanysource/1/']}
+ data = {'url': '/manytomanytarget/1/', 'name': 'target-1', 'sources': ['/manytomanysource/1/']}
instance = ManyToManyTarget.objects.get(pk=1)
serializer = ManyToManyTargetSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -120,48 +122,48 @@ class HyperlinkedManyToManyTests(TestCase):
queryset = ManyToManyTarget.objects.all()
serializer = ManyToManyTargetSerializer(queryset)
expected = [
- {'url': '/manytomanytarget/1/', 'name': u'target-1', 'sources': ['/manytomanysource/1/']},
- {'url': '/manytomanytarget/2/', 'name': u'target-2', 'sources': ['/manytomanysource/2/', '/manytomanysource/3/']},
- {'url': '/manytomanytarget/3/', 'name': u'target-3', 'sources': ['/manytomanysource/3/']}
+ {'url': '/manytomanytarget/1/', 'name': 'target-1', 'sources': ['/manytomanysource/1/']},
+ {'url': '/manytomanytarget/2/', 'name': 'target-2', 'sources': ['/manytomanysource/2/', '/manytomanysource/3/']},
+ {'url': '/manytomanytarget/3/', 'name': 'target-3', 'sources': ['/manytomanysource/3/']}
]
self.assertEquals(serializer.data, expected)
def test_many_to_many_create(self):
- data = {'url': '/manytomanysource/4/', 'name': u'source-4', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/3/']}
+ data = {'url': '/manytomanysource/4/', 'name': 'source-4', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/3/']}
serializer = ManyToManySourceSerializer(data=data)
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, data)
- self.assertEqual(obj.name, u'source-4')
+ self.assertEqual(obj.name, 'source-4')
# Ensure source 4 is added, and everything else is as expected
queryset = ManyToManySource.objects.all()
serializer = ManyToManySourceSerializer(queryset)
expected = [
- {'url': '/manytomanysource/1/', 'name': u'source-1', 'targets': ['/manytomanytarget/1/']},
- {'url': '/manytomanysource/2/', 'name': u'source-2', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/']},
- {'url': '/manytomanysource/3/', 'name': u'source-3', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']},
- {'url': '/manytomanysource/4/', 'name': u'source-4', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/3/']}
+ {'url': '/manytomanysource/1/', 'name': 'source-1', 'targets': ['/manytomanytarget/1/']},
+ {'url': '/manytomanysource/2/', 'name': 'source-2', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/']},
+ {'url': '/manytomanysource/3/', 'name': 'source-3', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/2/', '/manytomanytarget/3/']},
+ {'url': '/manytomanysource/4/', 'name': 'source-4', 'targets': ['/manytomanytarget/1/', '/manytomanytarget/3/']}
]
self.assertEquals(serializer.data, expected)
def test_reverse_many_to_many_create(self):
- data = {'url': '/manytomanytarget/4/', 'name': u'target-4', 'sources': ['/manytomanysource/1/', '/manytomanysource/3/']}
+ data = {'url': '/manytomanytarget/4/', 'name': 'target-4', 'sources': ['/manytomanysource/1/', '/manytomanysource/3/']}
serializer = ManyToManyTargetSerializer(data=data)
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, data)
- self.assertEqual(obj.name, u'target-4')
+ self.assertEqual(obj.name, 'target-4')
# Ensure target 4 is added, and everything else is as expected
queryset = ManyToManyTarget.objects.all()
serializer = ManyToManyTargetSerializer(queryset)
expected = [
- {'url': '/manytomanytarget/1/', 'name': u'target-1', 'sources': ['/manytomanysource/1/', '/manytomanysource/2/', '/manytomanysource/3/']},
- {'url': '/manytomanytarget/2/', 'name': u'target-2', 'sources': ['/manytomanysource/2/', '/manytomanysource/3/']},
- {'url': '/manytomanytarget/3/', 'name': u'target-3', 'sources': ['/manytomanysource/3/']},
- {'url': '/manytomanytarget/4/', 'name': u'target-4', 'sources': ['/manytomanysource/1/', '/manytomanysource/3/']}
+ {'url': '/manytomanytarget/1/', 'name': 'target-1', 'sources': ['/manytomanysource/1/', '/manytomanysource/2/', '/manytomanysource/3/']},
+ {'url': '/manytomanytarget/2/', 'name': 'target-2', 'sources': ['/manytomanysource/2/', '/manytomanysource/3/']},
+ {'url': '/manytomanytarget/3/', 'name': 'target-3', 'sources': ['/manytomanysource/3/']},
+ {'url': '/manytomanytarget/4/', 'name': 'target-4', 'sources': ['/manytomanysource/1/', '/manytomanysource/3/']}
]
self.assertEquals(serializer.data, expected)
@@ -182,9 +184,9 @@ class HyperlinkedForeignKeyTests(TestCase):
queryset = ForeignKeySource.objects.all()
serializer = ForeignKeySourceSerializer(queryset)
expected = [
- {'url': '/foreignkeysource/1/', 'name': u'source-1', 'target': '/foreignkeytarget/1/'},
- {'url': '/foreignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'},
- {'url': '/foreignkeysource/3/', 'name': u'source-3', 'target': '/foreignkeytarget/1/'}
+ {'url': '/foreignkeysource/1/', 'name': 'source-1', 'target': '/foreignkeytarget/1/'},
+ {'url': '/foreignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'},
+ {'url': '/foreignkeysource/3/', 'name': 'source-3', 'target': '/foreignkeytarget/1/'}
]
self.assertEquals(serializer.data, expected)
@@ -192,13 +194,13 @@ class HyperlinkedForeignKeyTests(TestCase):
queryset = ForeignKeyTarget.objects.all()
serializer = ForeignKeyTargetSerializer(queryset)
expected = [
- {'url': '/foreignkeytarget/1/', 'name': u'target-1', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/2/', '/foreignkeysource/3/']},
- {'url': '/foreignkeytarget/2/', 'name': u'target-2', 'sources': []},
+ {'url': '/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/2/', '/foreignkeysource/3/']},
+ {'url': '/foreignkeytarget/2/', 'name': 'target-2', 'sources': []},
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_update(self):
- data = {'url': '/foreignkeysource/1/', 'name': u'source-1', 'target': '/foreignkeytarget/2/'}
+ data = {'url': '/foreignkeysource/1/', 'name': 'source-1', 'target': '/foreignkeytarget/2/'}
instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -209,21 +211,21 @@ class HyperlinkedForeignKeyTests(TestCase):
queryset = ForeignKeySource.objects.all()
serializer = ForeignKeySourceSerializer(queryset)
expected = [
- {'url': '/foreignkeysource/1/', 'name': u'source-1', 'target': '/foreignkeytarget/2/'},
- {'url': '/foreignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'},
- {'url': '/foreignkeysource/3/', 'name': u'source-3', 'target': '/foreignkeytarget/1/'}
+ {'url': '/foreignkeysource/1/', 'name': 'source-1', 'target': '/foreignkeytarget/2/'},
+ {'url': '/foreignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'},
+ {'url': '/foreignkeysource/3/', 'name': 'source-3', 'target': '/foreignkeytarget/1/'}
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_update_incorrect_type(self):
- data = {'url': '/foreignkeysource/1/', 'name': u'source-1', 'target': 2}
+ data = {'url': '/foreignkeysource/1/', 'name': 'source-1', 'target': 2}
instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data)
self.assertFalse(serializer.is_valid())
- self.assertEquals(serializer.errors, {'target': [u'Incorrect type. Expected url string, received int.']})
+ self.assertEquals(serializer.errors, {'target': ['Incorrect type. Expected url string, received int.']})
def test_reverse_foreign_key_update(self):
- data = {'url': '/foreignkeytarget/2/', 'name': u'target-2', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']}
+ data = {'url': '/foreignkeytarget/2/', 'name': 'target-2', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']}
instance = ForeignKeyTarget.objects.get(pk=2)
serializer = ForeignKeyTargetSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -232,8 +234,8 @@ class HyperlinkedForeignKeyTests(TestCase):
queryset = ForeignKeyTarget.objects.all()
new_serializer = ForeignKeyTargetSerializer(queryset)
expected = [
- {'url': '/foreignkeytarget/1/', 'name': u'target-1', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/2/', '/foreignkeysource/3/']},
- {'url': '/foreignkeytarget/2/', 'name': u'target-2', 'sources': []},
+ {'url': '/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/2/', '/foreignkeysource/3/']},
+ {'url': '/foreignkeytarget/2/', 'name': 'target-2', 'sources': []},
]
self.assertEquals(new_serializer.data, expected)
@@ -244,54 +246,54 @@ class HyperlinkedForeignKeyTests(TestCase):
queryset = ForeignKeyTarget.objects.all()
serializer = ForeignKeyTargetSerializer(queryset)
expected = [
- {'url': '/foreignkeytarget/1/', 'name': u'target-1', 'sources': ['/foreignkeysource/2/']},
- {'url': '/foreignkeytarget/2/', 'name': u'target-2', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']},
+ {'url': '/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['/foreignkeysource/2/']},
+ {'url': '/foreignkeytarget/2/', 'name': 'target-2', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']},
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_create(self):
- data = {'url': '/foreignkeysource/4/', 'name': u'source-4', 'target': '/foreignkeytarget/2/'}
+ data = {'url': '/foreignkeysource/4/', 'name': 'source-4', 'target': '/foreignkeytarget/2/'}
serializer = ForeignKeySourceSerializer(data=data)
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, data)
- self.assertEqual(obj.name, u'source-4')
+ self.assertEqual(obj.name, 'source-4')
# Ensure source 1 is updated, and everything else is as expected
queryset = ForeignKeySource.objects.all()
serializer = ForeignKeySourceSerializer(queryset)
expected = [
- {'url': '/foreignkeysource/1/', 'name': u'source-1', 'target': '/foreignkeytarget/1/'},
- {'url': '/foreignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'},
- {'url': '/foreignkeysource/3/', 'name': u'source-3', 'target': '/foreignkeytarget/1/'},
- {'url': '/foreignkeysource/4/', 'name': u'source-4', 'target': '/foreignkeytarget/2/'},
+ {'url': '/foreignkeysource/1/', 'name': 'source-1', 'target': '/foreignkeytarget/1/'},
+ {'url': '/foreignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'},
+ {'url': '/foreignkeysource/3/', 'name': 'source-3', 'target': '/foreignkeytarget/1/'},
+ {'url': '/foreignkeysource/4/', 'name': 'source-4', 'target': '/foreignkeytarget/2/'},
]
self.assertEquals(serializer.data, expected)
def test_reverse_foreign_key_create(self):
- data = {'url': '/foreignkeytarget/3/', 'name': u'target-3', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']}
+ data = {'url': '/foreignkeytarget/3/', 'name': 'target-3', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']}
serializer = ForeignKeyTargetSerializer(data=data)
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, data)
- self.assertEqual(obj.name, u'target-3')
+ self.assertEqual(obj.name, 'target-3')
# Ensure target 4 is added, and everything else is as expected
queryset = ForeignKeyTarget.objects.all()
serializer = ForeignKeyTargetSerializer(queryset)
expected = [
- {'url': '/foreignkeytarget/1/', 'name': u'target-1', 'sources': ['/foreignkeysource/2/']},
- {'url': '/foreignkeytarget/2/', 'name': u'target-2', 'sources': []},
- {'url': '/foreignkeytarget/3/', 'name': u'target-3', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']},
+ {'url': '/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['/foreignkeysource/2/']},
+ {'url': '/foreignkeytarget/2/', 'name': 'target-2', 'sources': []},
+ {'url': '/foreignkeytarget/3/', 'name': 'target-3', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']},
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_update_with_invalid_null(self):
- data = {'url': '/foreignkeysource/1/', 'name': u'source-1', 'target': None}
+ data = {'url': '/foreignkeysource/1/', 'name': 'source-1', 'target': None}
instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data)
self.assertFalse(serializer.is_valid())
- self.assertEquals(serializer.errors, {'target': [u'This field is required.']})
+ self.assertEquals(serializer.errors, {'target': ['This field is required.']})
class HyperlinkedNullableForeignKeyTests(TestCase):
@@ -310,28 +312,28 @@ class HyperlinkedNullableForeignKeyTests(TestCase):
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': '/foreignkeytarget/1/'},
- {'url': '/nullableforeignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'},
- {'url': '/nullableforeignkeysource/3/', 'name': u'source-3', 'target': None},
+ {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': '/foreignkeytarget/1/'},
+ {'url': '/nullableforeignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'},
+ {'url': '/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None},
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_create_with_valid_null(self):
- data = {'url': '/nullableforeignkeysource/4/', 'name': u'source-4', 'target': None}
+ data = {'url': '/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None}
serializer = NullableForeignKeySourceSerializer(data=data)
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, data)
- self.assertEqual(obj.name, u'source-4')
+ self.assertEqual(obj.name, 'source-4')
# Ensure source 4 is created, and everything else is as expected
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': '/foreignkeytarget/1/'},
- {'url': '/nullableforeignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'},
- {'url': '/nullableforeignkeysource/3/', 'name': u'source-3', 'target': None},
- {'url': '/nullableforeignkeysource/4/', 'name': u'source-4', 'target': None}
+ {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': '/foreignkeytarget/1/'},
+ {'url': '/nullableforeignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'},
+ {'url': '/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None},
+ {'url': '/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None}
]
self.assertEquals(serializer.data, expected)
@@ -340,27 +342,27 @@ class HyperlinkedNullableForeignKeyTests(TestCase):
The emptystring should be interpreted as null in the context
of relationships.
"""
- data = {'url': '/nullableforeignkeysource/4/', 'name': u'source-4', 'target': ''}
- expected_data = {'url': '/nullableforeignkeysource/4/', 'name': u'source-4', 'target': None}
+ data = {'url': '/nullableforeignkeysource/4/', 'name': 'source-4', 'target': ''}
+ expected_data = {'url': '/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None}
serializer = NullableForeignKeySourceSerializer(data=data)
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, expected_data)
- self.assertEqual(obj.name, u'source-4')
+ self.assertEqual(obj.name, 'source-4')
# Ensure source 4 is created, and everything else is as expected
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': '/foreignkeytarget/1/'},
- {'url': '/nullableforeignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'},
- {'url': '/nullableforeignkeysource/3/', 'name': u'source-3', 'target': None},
- {'url': '/nullableforeignkeysource/4/', 'name': u'source-4', 'target': None}
+ {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': '/foreignkeytarget/1/'},
+ {'url': '/nullableforeignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'},
+ {'url': '/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None},
+ {'url': '/nullableforeignkeysource/4/', 'name': 'source-4', 'target': None}
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_update_with_valid_null(self):
- data = {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': None}
+ data = {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None}
instance = NullableForeignKeySource.objects.get(pk=1)
serializer = NullableForeignKeySourceSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -371,9 +373,9 @@ class HyperlinkedNullableForeignKeyTests(TestCase):
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': None},
- {'url': '/nullableforeignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'},
- {'url': '/nullableforeignkeysource/3/', 'name': u'source-3', 'target': None},
+ {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None},
+ {'url': '/nullableforeignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'},
+ {'url': '/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None},
]
self.assertEquals(serializer.data, expected)
@@ -382,8 +384,8 @@ class HyperlinkedNullableForeignKeyTests(TestCase):
The emptystring should be interpreted as null in the context
of relationships.
"""
- data = {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': ''}
- expected_data = {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': None}
+ data = {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': ''}
+ expected_data = {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None}
instance = NullableForeignKeySource.objects.get(pk=1)
serializer = NullableForeignKeySourceSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -394,9 +396,9 @@ class HyperlinkedNullableForeignKeyTests(TestCase):
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'url': '/nullableforeignkeysource/1/', 'name': u'source-1', 'target': None},
- {'url': '/nullableforeignkeysource/2/', 'name': u'source-2', 'target': '/foreignkeytarget/1/'},
- {'url': '/nullableforeignkeysource/3/', 'name': u'source-3', 'target': None},
+ {'url': '/nullableforeignkeysource/1/', 'name': 'source-1', 'target': None},
+ {'url': '/nullableforeignkeysource/2/', 'name': 'source-2', 'target': '/foreignkeytarget/1/'},
+ {'url': '/nullableforeignkeysource/3/', 'name': 'source-3', 'target': None},
]
self.assertEquals(serializer.data, expected)
@@ -405,7 +407,7 @@ class HyperlinkedNullableForeignKeyTests(TestCase):
# and cannot be arbitrarily set.
# def test_reverse_foreign_key_update(self):
- # data = {'id': 1, 'name': u'target-1', 'sources': [1]}
+ # data = {'id': 1, 'name': 'target-1', 'sources': [1]}
# instance = ForeignKeyTarget.objects.get(pk=1)
# serializer = ForeignKeyTargetSerializer(instance, data=data)
# self.assertTrue(serializer.is_valid())
@@ -416,8 +418,8 @@ class HyperlinkedNullableForeignKeyTests(TestCase):
# queryset = ForeignKeyTarget.objects.all()
# serializer = ForeignKeyTargetSerializer(queryset)
# expected = [
- # {'id': 1, 'name': u'target-1', 'sources': [1]},
- # {'id': 2, 'name': u'target-2', 'sources': []},
+ # {'id': 1, 'name': 'target-1', 'sources': [1]},
+ # {'id': 2, 'name': 'target-2', 'sources': []},
# ]
# self.assertEquals(serializer.data, expected)
@@ -437,7 +439,7 @@ class HyperlinkedNullableOneToOneTests(TestCase):
queryset = OneToOneTarget.objects.all()
serializer = NullableOneToOneTargetSerializer(queryset)
expected = [
- {'url': '/onetoonetarget/1/', 'name': u'target-1', 'nullable_source': '/nullableonetoonesource/1/'},
- {'url': '/onetoonetarget/2/', 'name': u'target-2', 'nullable_source': None},
+ {'url': '/onetoonetarget/1/', 'name': 'target-1', 'nullable_source': '/nullableonetoonesource/1/'},
+ {'url': '/onetoonetarget/2/', 'name': 'target-2', 'nullable_source': None},
]
self.assertEquals(serializer.data, expected)
diff --git a/rest_framework/tests/relations_nested.py b/rest_framework/tests/relations_nested.py
index 0e129fae..e9051e71 100644
--- a/rest_framework/tests/relations_nested.py
+++ b/rest_framework/tests/relations_nested.py
@@ -1,3 +1,4 @@
+from __future__ import unicode_literals
from django.test import TestCase
from rest_framework import serializers
from rest_framework.tests.models import ForeignKeyTarget, ForeignKeySource, NullableForeignKeySource, OneToOneTarget, NullableOneToOneSource
@@ -53,9 +54,9 @@ class ReverseForeignKeyTests(TestCase):
queryset = ForeignKeySource.objects.all()
serializer = ForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': {'id': 1, 'name': u'target-1'}},
- {'id': 2, 'name': u'source-2', 'target': {'id': 1, 'name': u'target-1'}},
- {'id': 3, 'name': u'source-3', 'target': {'id': 1, 'name': u'target-1'}},
+ {'id': 1, 'name': 'source-1', 'target': {'id': 1, 'name': 'target-1'}},
+ {'id': 2, 'name': 'source-2', 'target': {'id': 1, 'name': 'target-1'}},
+ {'id': 3, 'name': 'source-3', 'target': {'id': 1, 'name': 'target-1'}},
]
self.assertEquals(serializer.data, expected)
@@ -63,12 +64,12 @@ class ReverseForeignKeyTests(TestCase):
queryset = ForeignKeyTarget.objects.all()
serializer = ForeignKeyTargetSerializer(queryset)
expected = [
- {'id': 1, 'name': u'target-1', 'sources': [
- {'id': 1, 'name': u'source-1', 'target': 1},
- {'id': 2, 'name': u'source-2', 'target': 1},
- {'id': 3, 'name': u'source-3', 'target': 1},
+ {'id': 1, 'name': 'target-1', 'sources': [
+ {'id': 1, 'name': 'source-1', 'target': 1},
+ {'id': 2, 'name': 'source-2', 'target': 1},
+ {'id': 3, 'name': 'source-3', 'target': 1},
]},
- {'id': 2, 'name': u'target-2', 'sources': [
+ {'id': 2, 'name': 'target-2', 'sources': [
]}
]
self.assertEquals(serializer.data, expected)
@@ -88,9 +89,9 @@ class NestedNullableForeignKeyTests(TestCase):
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': {'id': 1, 'name': u'target-1'}},
- {'id': 2, 'name': u'source-2', 'target': {'id': 1, 'name': u'target-1'}},
- {'id': 3, 'name': u'source-3', 'target': None},
+ {'id': 1, 'name': 'source-1', 'target': {'id': 1, 'name': 'target-1'}},
+ {'id': 2, 'name': 'source-2', 'target': {'id': 1, 'name': 'target-1'}},
+ {'id': 3, 'name': 'source-3', 'target': None},
]
self.assertEquals(serializer.data, expected)
@@ -108,7 +109,7 @@ class NestedNullableOneToOneTests(TestCase):
queryset = OneToOneTarget.objects.all()
serializer = NullableOneToOneTargetSerializer(queryset)
expected = [
- {'id': 1, 'name': u'target-1', 'nullable_source': {'id': 1, 'name': u'source-1', 'target': 1}},
- {'id': 2, 'name': u'target-2', 'nullable_source': None},
+ {'id': 1, 'name': 'target-1', 'nullable_source': {'id': 1, 'name': 'source-1', 'target': 1}},
+ {'id': 2, 'name': 'target-2', 'nullable_source': None},
]
self.assertEquals(serializer.data, expected)
diff --git a/rest_framework/tests/relations_pk.py b/rest_framework/tests/relations_pk.py
index 3391e60a..ca7ac17e 100644
--- a/rest_framework/tests/relations_pk.py
+++ b/rest_framework/tests/relations_pk.py
@@ -1,3 +1,6 @@
+from __future__ import unicode_literals
+
+from django.db import models
from django.test import TestCase
from rest_framework import serializers
from rest_framework.tests.models import ManyToManyTarget, ManyToManySource, ForeignKeyTarget, ForeignKeySource, NullableForeignKeySource, OneToOneTarget, NullableOneToOneSource
@@ -56,9 +59,9 @@ class PKManyToManyTests(TestCase):
queryset = ManyToManySource.objects.all()
serializer = ManyToManySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'targets': [1]},
- {'id': 2, 'name': u'source-2', 'targets': [1, 2]},
- {'id': 3, 'name': u'source-3', 'targets': [1, 2, 3]}
+ {'id': 1, 'name': 'source-1', 'targets': [1]},
+ {'id': 2, 'name': 'source-2', 'targets': [1, 2]},
+ {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]}
]
self.assertEquals(serializer.data, expected)
@@ -66,14 +69,14 @@ class PKManyToManyTests(TestCase):
queryset = ManyToManyTarget.objects.all()
serializer = ManyToManyTargetSerializer(queryset)
expected = [
- {'id': 1, 'name': u'target-1', 'sources': [1, 2, 3]},
- {'id': 2, 'name': u'target-2', 'sources': [2, 3]},
- {'id': 3, 'name': u'target-3', 'sources': [3]}
+ {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]},
+ {'id': 2, 'name': 'target-2', 'sources': [2, 3]},
+ {'id': 3, 'name': 'target-3', 'sources': [3]}
]
self.assertEquals(serializer.data, expected)
def test_many_to_many_update(self):
- data = {'id': 1, 'name': u'source-1', 'targets': [1, 2, 3]}
+ data = {'id': 1, 'name': 'source-1', 'targets': [1, 2, 3]}
instance = ManyToManySource.objects.get(pk=1)
serializer = ManyToManySourceSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -84,14 +87,14 @@ class PKManyToManyTests(TestCase):
queryset = ManyToManySource.objects.all()
serializer = ManyToManySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'targets': [1, 2, 3]},
- {'id': 2, 'name': u'source-2', 'targets': [1, 2]},
- {'id': 3, 'name': u'source-3', 'targets': [1, 2, 3]}
+ {'id': 1, 'name': 'source-1', 'targets': [1, 2, 3]},
+ {'id': 2, 'name': 'source-2', 'targets': [1, 2]},
+ {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]}
]
self.assertEquals(serializer.data, expected)
def test_reverse_many_to_many_update(self):
- data = {'id': 1, 'name': u'target-1', 'sources': [1]}
+ data = {'id': 1, 'name': 'target-1', 'sources': [1]}
instance = ManyToManyTarget.objects.get(pk=1)
serializer = ManyToManyTargetSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -102,47 +105,47 @@ class PKManyToManyTests(TestCase):
queryset = ManyToManyTarget.objects.all()
serializer = ManyToManyTargetSerializer(queryset)
expected = [
- {'id': 1, 'name': u'target-1', 'sources': [1]},
- {'id': 2, 'name': u'target-2', 'sources': [2, 3]},
- {'id': 3, 'name': u'target-3', 'sources': [3]}
+ {'id': 1, 'name': 'target-1', 'sources': [1]},
+ {'id': 2, 'name': 'target-2', 'sources': [2, 3]},
+ {'id': 3, 'name': 'target-3', 'sources': [3]}
]
self.assertEquals(serializer.data, expected)
def test_many_to_many_create(self):
- data = {'id': 4, 'name': u'source-4', 'targets': [1, 3]}
+ data = {'id': 4, 'name': 'source-4', 'targets': [1, 3]}
serializer = ManyToManySourceSerializer(data=data)
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, data)
- self.assertEqual(obj.name, u'source-4')
+ self.assertEqual(obj.name, 'source-4')
# Ensure source 4 is added, and everything else is as expected
queryset = ManyToManySource.objects.all()
serializer = ManyToManySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'targets': [1]},
- {'id': 2, 'name': u'source-2', 'targets': [1, 2]},
- {'id': 3, 'name': u'source-3', 'targets': [1, 2, 3]},
- {'id': 4, 'name': u'source-4', 'targets': [1, 3]},
+ {'id': 1, 'name': 'source-1', 'targets': [1]},
+ {'id': 2, 'name': 'source-2', 'targets': [1, 2]},
+ {'id': 3, 'name': 'source-3', 'targets': [1, 2, 3]},
+ {'id': 4, 'name': 'source-4', 'targets': [1, 3]},
]
self.assertEquals(serializer.data, expected)
def test_reverse_many_to_many_create(self):
- data = {'id': 4, 'name': u'target-4', 'sources': [1, 3]}
+ data = {'id': 4, 'name': 'target-4', 'sources': [1, 3]}
serializer = ManyToManyTargetSerializer(data=data)
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, data)
- self.assertEqual(obj.name, u'target-4')
+ self.assertEqual(obj.name, 'target-4')
# Ensure target 4 is added, and everything else is as expected
queryset = ManyToManyTarget.objects.all()
serializer = ManyToManyTargetSerializer(queryset)
expected = [
- {'id': 1, 'name': u'target-1', 'sources': [1, 2, 3]},
- {'id': 2, 'name': u'target-2', 'sources': [2, 3]},
- {'id': 3, 'name': u'target-3', 'sources': [3]},
- {'id': 4, 'name': u'target-4', 'sources': [1, 3]}
+ {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]},
+ {'id': 2, 'name': 'target-2', 'sources': [2, 3]},
+ {'id': 3, 'name': 'target-3', 'sources': [3]},
+ {'id': 4, 'name': 'target-4', 'sources': [1, 3]}
]
self.assertEquals(serializer.data, expected)
@@ -161,9 +164,9 @@ class PKForeignKeyTests(TestCase):
queryset = ForeignKeySource.objects.all()
serializer = ForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': 1},
- {'id': 2, 'name': u'source-2', 'target': 1},
- {'id': 3, 'name': u'source-3', 'target': 1}
+ {'id': 1, 'name': 'source-1', 'target': 1},
+ {'id': 2, 'name': 'source-2', 'target': 1},
+ {'id': 3, 'name': 'source-3', 'target': 1}
]
self.assertEquals(serializer.data, expected)
@@ -171,13 +174,13 @@ class PKForeignKeyTests(TestCase):
queryset = ForeignKeyTarget.objects.all()
serializer = ForeignKeyTargetSerializer(queryset)
expected = [
- {'id': 1, 'name': u'target-1', 'sources': [1, 2, 3]},
- {'id': 2, 'name': u'target-2', 'sources': []},
+ {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]},
+ {'id': 2, 'name': 'target-2', 'sources': []},
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_update(self):
- data = {'id': 1, 'name': u'source-1', 'target': 2}
+ data = {'id': 1, 'name': 'source-1', 'target': 2}
instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -188,21 +191,21 @@ class PKForeignKeyTests(TestCase):
queryset = ForeignKeySource.objects.all()
serializer = ForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': 2},
- {'id': 2, 'name': u'source-2', 'target': 1},
- {'id': 3, 'name': u'source-3', 'target': 1}
+ {'id': 1, 'name': 'source-1', 'target': 2},
+ {'id': 2, 'name': 'source-2', 'target': 1},
+ {'id': 3, 'name': 'source-3', 'target': 1}
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_update_incorrect_type(self):
- data = {'id': 1, 'name': u'source-1', 'target': 'foo'}
+ data = {'id': 1, 'name': 'source-1', 'target': 'foo'}
instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data)
self.assertFalse(serializer.is_valid())
- self.assertEquals(serializer.errors, {'target': [u'Incorrect type. Expected pk value, received str.']})
+ self.assertEquals(serializer.errors, {'target': ['Incorrect type. Expected pk value, received str.']})
def test_reverse_foreign_key_update(self):
- data = {'id': 2, 'name': u'target-2', 'sources': [1, 3]}
+ data = {'id': 2, 'name': 'target-2', 'sources': [1, 3]}
instance = ForeignKeyTarget.objects.get(pk=2)
serializer = ForeignKeyTargetSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -211,8 +214,8 @@ class PKForeignKeyTests(TestCase):
queryset = ForeignKeyTarget.objects.all()
new_serializer = ForeignKeyTargetSerializer(queryset)
expected = [
- {'id': 1, 'name': u'target-1', 'sources': [1, 2, 3]},
- {'id': 2, 'name': u'target-2', 'sources': []},
+ {'id': 1, 'name': 'target-1', 'sources': [1, 2, 3]},
+ {'id': 2, 'name': 'target-2', 'sources': []},
]
self.assertEquals(new_serializer.data, expected)
@@ -223,54 +226,54 @@ class PKForeignKeyTests(TestCase):
queryset = ForeignKeyTarget.objects.all()
serializer = ForeignKeyTargetSerializer(queryset)
expected = [
- {'id': 1, 'name': u'target-1', 'sources': [2]},
- {'id': 2, 'name': u'target-2', 'sources': [1, 3]},
+ {'id': 1, 'name': 'target-1', 'sources': [2]},
+ {'id': 2, 'name': 'target-2', 'sources': [1, 3]},
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_create(self):
- data = {'id': 4, 'name': u'source-4', 'target': 2}
+ data = {'id': 4, 'name': 'source-4', 'target': 2}
serializer = ForeignKeySourceSerializer(data=data)
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, data)
- self.assertEqual(obj.name, u'source-4')
+ self.assertEqual(obj.name, 'source-4')
# Ensure source 4 is added, and everything else is as expected
queryset = ForeignKeySource.objects.all()
serializer = ForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': 1},
- {'id': 2, 'name': u'source-2', 'target': 1},
- {'id': 3, 'name': u'source-3', 'target': 1},
- {'id': 4, 'name': u'source-4', 'target': 2},
+ {'id': 1, 'name': 'source-1', 'target': 1},
+ {'id': 2, 'name': 'source-2', 'target': 1},
+ {'id': 3, 'name': 'source-3', 'target': 1},
+ {'id': 4, 'name': 'source-4', 'target': 2},
]
self.assertEquals(serializer.data, expected)
def test_reverse_foreign_key_create(self):
- data = {'id': 3, 'name': u'target-3', 'sources': [1, 3]}
+ data = {'id': 3, 'name': 'target-3', 'sources': [1, 3]}
serializer = ForeignKeyTargetSerializer(data=data)
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, data)
- self.assertEqual(obj.name, u'target-3')
+ self.assertEqual(obj.name, 'target-3')
# Ensure target 3 is added, and everything else is as expected
queryset = ForeignKeyTarget.objects.all()
serializer = ForeignKeyTargetSerializer(queryset)
expected = [
- {'id': 1, 'name': u'target-1', 'sources': [2]},
- {'id': 2, 'name': u'target-2', 'sources': []},
- {'id': 3, 'name': u'target-3', 'sources': [1, 3]},
+ {'id': 1, 'name': 'target-1', 'sources': [2]},
+ {'id': 2, 'name': 'target-2', 'sources': []},
+ {'id': 3, 'name': 'target-3', 'sources': [1, 3]},
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_update_with_invalid_null(self):
- data = {'id': 1, 'name': u'source-1', 'target': None}
+ data = {'id': 1, 'name': 'source-1', 'target': None}
instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data)
self.assertFalse(serializer.is_valid())
- self.assertEquals(serializer.errors, {'target': [u'Value may not be null']})
+ self.assertEquals(serializer.errors, {'target': ['Value may not be null']})
class PKNullableForeignKeyTests(TestCase):
@@ -287,28 +290,28 @@ class PKNullableForeignKeyTests(TestCase):
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': 1},
- {'id': 2, 'name': u'source-2', 'target': 1},
- {'id': 3, 'name': u'source-3', 'target': None},
+ {'id': 1, 'name': 'source-1', 'target': 1},
+ {'id': 2, 'name': 'source-2', 'target': 1},
+ {'id': 3, 'name': 'source-3', 'target': None},
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_create_with_valid_null(self):
- data = {'id': 4, 'name': u'source-4', 'target': None}
+ data = {'id': 4, 'name': 'source-4', 'target': None}
serializer = NullableForeignKeySourceSerializer(data=data)
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, data)
- self.assertEqual(obj.name, u'source-4')
+ self.assertEqual(obj.name, 'source-4')
# Ensure source 4 is created, and everything else is as expected
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': 1},
- {'id': 2, 'name': u'source-2', 'target': 1},
- {'id': 3, 'name': u'source-3', 'target': None},
- {'id': 4, 'name': u'source-4', 'target': None}
+ {'id': 1, 'name': 'source-1', 'target': 1},
+ {'id': 2, 'name': 'source-2', 'target': 1},
+ {'id': 3, 'name': 'source-3', 'target': None},
+ {'id': 4, 'name': 'source-4', 'target': None}
]
self.assertEquals(serializer.data, expected)
@@ -317,27 +320,27 @@ class PKNullableForeignKeyTests(TestCase):
The emptystring should be interpreted as null in the context
of relationships.
"""
- data = {'id': 4, 'name': u'source-4', 'target': ''}
- expected_data = {'id': 4, 'name': u'source-4', 'target': None}
+ data = {'id': 4, 'name': 'source-4', 'target': ''}
+ expected_data = {'id': 4, 'name': 'source-4', 'target': None}
serializer = NullableForeignKeySourceSerializer(data=data)
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, expected_data)
- self.assertEqual(obj.name, u'source-4')
+ self.assertEqual(obj.name, 'source-4')
# Ensure source 4 is created, and everything else is as expected
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': 1},
- {'id': 2, 'name': u'source-2', 'target': 1},
- {'id': 3, 'name': u'source-3', 'target': None},
- {'id': 4, 'name': u'source-4', 'target': None}
+ {'id': 1, 'name': 'source-1', 'target': 1},
+ {'id': 2, 'name': 'source-2', 'target': 1},
+ {'id': 3, 'name': 'source-3', 'target': None},
+ {'id': 4, 'name': 'source-4', 'target': None}
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_update_with_valid_null(self):
- data = {'id': 1, 'name': u'source-1', 'target': None}
+ data = {'id': 1, 'name': 'source-1', 'target': None}
instance = NullableForeignKeySource.objects.get(pk=1)
serializer = NullableForeignKeySourceSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -348,9 +351,9 @@ class PKNullableForeignKeyTests(TestCase):
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': None},
- {'id': 2, 'name': u'source-2', 'target': 1},
- {'id': 3, 'name': u'source-3', 'target': None}
+ {'id': 1, 'name': 'source-1', 'target': None},
+ {'id': 2, 'name': 'source-2', 'target': 1},
+ {'id': 3, 'name': 'source-3', 'target': None}
]
self.assertEquals(serializer.data, expected)
@@ -359,8 +362,8 @@ class PKNullableForeignKeyTests(TestCase):
The emptystring should be interpreted as null in the context
of relationships.
"""
- data = {'id': 1, 'name': u'source-1', 'target': ''}
- expected_data = {'id': 1, 'name': u'source-1', 'target': None}
+ data = {'id': 1, 'name': 'source-1', 'target': ''}
+ expected_data = {'id': 1, 'name': 'source-1', 'target': None}
instance = NullableForeignKeySource.objects.get(pk=1)
serializer = NullableForeignKeySourceSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -371,9 +374,9 @@ class PKNullableForeignKeyTests(TestCase):
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': None},
- {'id': 2, 'name': u'source-2', 'target': 1},
- {'id': 3, 'name': u'source-3', 'target': None}
+ {'id': 1, 'name': 'source-1', 'target': None},
+ {'id': 2, 'name': 'source-2', 'target': 1},
+ {'id': 3, 'name': 'source-3', 'target': None}
]
self.assertEquals(serializer.data, expected)
@@ -382,7 +385,7 @@ class PKNullableForeignKeyTests(TestCase):
# and cannot be arbitrarily set.
# def test_reverse_foreign_key_update(self):
- # data = {'id': 1, 'name': u'target-1', 'sources': [1]}
+ # data = {'id': 1, 'name': 'target-1', 'sources': [1]}
# instance = ForeignKeyTarget.objects.get(pk=1)
# serializer = ForeignKeyTargetSerializer(instance, data=data)
# self.assertTrue(serializer.is_valid())
@@ -393,8 +396,8 @@ class PKNullableForeignKeyTests(TestCase):
# queryset = ForeignKeyTarget.objects.all()
# serializer = ForeignKeyTargetSerializer(queryset)
# expected = [
- # {'id': 1, 'name': u'target-1', 'sources': [1]},
- # {'id': 2, 'name': u'target-2', 'sources': []},
+ # {'id': 1, 'name': 'target-1', 'sources': [1]},
+ # {'id': 2, 'name': 'target-2', 'sources': []},
# ]
# self.assertEquals(serializer.data, expected)
@@ -412,7 +415,7 @@ class PKNullableOneToOneTests(TestCase):
queryset = OneToOneTarget.objects.all()
serializer = NullableOneToOneTargetSerializer(queryset)
expected = [
- {'id': 1, 'name': u'target-1', 'nullable_source': 1},
- {'id': 2, 'name': u'target-2', 'nullable_source': None},
+ {'id': 1, 'name': 'target-1', 'nullable_source': 1},
+ {'id': 2, 'name': 'target-2', 'nullable_source': None},
]
self.assertEquals(serializer.data, expected)
diff --git a/rest_framework/tests/relations_slug.py b/rest_framework/tests/relations_slug.py
index 34d1f82a..c5558ec5 100644
--- a/rest_framework/tests/relations_slug.py
+++ b/rest_framework/tests/relations_slug.py
@@ -39,9 +39,9 @@ class PKForeignKeyTests(TestCase):
queryset = ForeignKeySource.objects.all()
serializer = ForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': 'target-1'},
- {'id': 2, 'name': u'source-2', 'target': 'target-1'},
- {'id': 3, 'name': u'source-3', 'target': 'target-1'}
+ {'id': 1, 'name': 'source-1', 'target': 'target-1'},
+ {'id': 2, 'name': 'source-2', 'target': 'target-1'},
+ {'id': 3, 'name': 'source-3', 'target': 'target-1'}
]
self.assertEquals(serializer.data, expected)
@@ -49,13 +49,13 @@ class PKForeignKeyTests(TestCase):
queryset = ForeignKeyTarget.objects.all()
serializer = ForeignKeyTargetSerializer(queryset)
expected = [
- {'id': 1, 'name': u'target-1', 'sources': ['source-1', 'source-2', 'source-3']},
- {'id': 2, 'name': u'target-2', 'sources': []},
+ {'id': 1, 'name': 'target-1', 'sources': ['source-1', 'source-2', 'source-3']},
+ {'id': 2, 'name': 'target-2', 'sources': []},
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_update(self):
- data = {'id': 1, 'name': u'source-1', 'target': 'target-2'}
+ data = {'id': 1, 'name': 'source-1', 'target': 'target-2'}
instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -66,21 +66,21 @@ class PKForeignKeyTests(TestCase):
queryset = ForeignKeySource.objects.all()
serializer = ForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': 'target-2'},
- {'id': 2, 'name': u'source-2', 'target': 'target-1'},
- {'id': 3, 'name': u'source-3', 'target': 'target-1'}
+ {'id': 1, 'name': 'source-1', 'target': 'target-2'},
+ {'id': 2, 'name': 'source-2', 'target': 'target-1'},
+ {'id': 3, 'name': 'source-3', 'target': 'target-1'}
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_update_incorrect_type(self):
- data = {'id': 1, 'name': u'source-1', 'target': 123}
+ data = {'id': 1, 'name': 'source-1', 'target': 123}
instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data)
self.assertFalse(serializer.is_valid())
- self.assertEquals(serializer.errors, {'target': [u'Object with name=123 does not exist.']})
+ self.assertEquals(serializer.errors, {'target': ['Object with name=123 does not exist.']})
def test_reverse_foreign_key_update(self):
- data = {'id': 2, 'name': u'target-2', 'sources': ['source-1', 'source-3']}
+ data = {'id': 2, 'name': 'target-2', 'sources': ['source-1', 'source-3']}
instance = ForeignKeyTarget.objects.get(pk=2)
serializer = ForeignKeyTargetSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -89,8 +89,8 @@ class PKForeignKeyTests(TestCase):
queryset = ForeignKeyTarget.objects.all()
new_serializer = ForeignKeyTargetSerializer(queryset)
expected = [
- {'id': 1, 'name': u'target-1', 'sources': ['source-1', 'source-2', 'source-3']},
- {'id': 2, 'name': u'target-2', 'sources': []},
+ {'id': 1, 'name': 'target-1', 'sources': ['source-1', 'source-2', 'source-3']},
+ {'id': 2, 'name': 'target-2', 'sources': []},
]
self.assertEquals(new_serializer.data, expected)
@@ -101,55 +101,55 @@ class PKForeignKeyTests(TestCase):
queryset = ForeignKeyTarget.objects.all()
serializer = ForeignKeyTargetSerializer(queryset)
expected = [
- {'id': 1, 'name': u'target-1', 'sources': ['source-2']},
- {'id': 2, 'name': u'target-2', 'sources': ['source-1', 'source-3']},
+ {'id': 1, 'name': 'target-1', 'sources': ['source-2']},
+ {'id': 2, 'name': 'target-2', 'sources': ['source-1', 'source-3']},
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_create(self):
- data = {'id': 4, 'name': u'source-4', 'target': 'target-2'}
+ data = {'id': 4, 'name': 'source-4', 'target': 'target-2'}
serializer = ForeignKeySourceSerializer(data=data)
serializer.is_valid()
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, data)
- self.assertEqual(obj.name, u'source-4')
+ self.assertEqual(obj.name, 'source-4')
# Ensure source 4 is added, and everything else is as expected
queryset = ForeignKeySource.objects.all()
serializer = ForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': 'target-1'},
- {'id': 2, 'name': u'source-2', 'target': 'target-1'},
- {'id': 3, 'name': u'source-3', 'target': 'target-1'},
- {'id': 4, 'name': u'source-4', 'target': 'target-2'},
+ {'id': 1, 'name': 'source-1', 'target': 'target-1'},
+ {'id': 2, 'name': 'source-2', 'target': 'target-1'},
+ {'id': 3, 'name': 'source-3', 'target': 'target-1'},
+ {'id': 4, 'name': 'source-4', 'target': 'target-2'},
]
self.assertEquals(serializer.data, expected)
def test_reverse_foreign_key_create(self):
- data = {'id': 3, 'name': u'target-3', 'sources': ['source-1', 'source-3']}
+ data = {'id': 3, 'name': 'target-3', 'sources': ['source-1', 'source-3']}
serializer = ForeignKeyTargetSerializer(data=data)
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, data)
- self.assertEqual(obj.name, u'target-3')
+ self.assertEqual(obj.name, 'target-3')
# Ensure target 3 is added, and everything else is as expected
queryset = ForeignKeyTarget.objects.all()
serializer = ForeignKeyTargetSerializer(queryset)
expected = [
- {'id': 1, 'name': u'target-1', 'sources': ['source-2']},
- {'id': 2, 'name': u'target-2', 'sources': []},
- {'id': 3, 'name': u'target-3', 'sources': ['source-1', 'source-3']},
+ {'id': 1, 'name': 'target-1', 'sources': ['source-2']},
+ {'id': 2, 'name': 'target-2', 'sources': []},
+ {'id': 3, 'name': 'target-3', 'sources': ['source-1', 'source-3']},
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_update_with_invalid_null(self):
- data = {'id': 1, 'name': u'source-1', 'target': None}
+ data = {'id': 1, 'name': 'source-1', 'target': None}
instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data)
self.assertFalse(serializer.is_valid())
- self.assertEquals(serializer.errors, {'target': [u'This field is required.']})
+ self.assertEquals(serializer.errors, {'target': ['This field is required.']})
class SlugNullableForeignKeyTests(TestCase):
@@ -166,28 +166,28 @@ class SlugNullableForeignKeyTests(TestCase):
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': 'target-1'},
- {'id': 2, 'name': u'source-2', 'target': 'target-1'},
- {'id': 3, 'name': u'source-3', 'target': None},
+ {'id': 1, 'name': 'source-1', 'target': 'target-1'},
+ {'id': 2, 'name': 'source-2', 'target': 'target-1'},
+ {'id': 3, 'name': 'source-3', 'target': None},
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_create_with_valid_null(self):
- data = {'id': 4, 'name': u'source-4', 'target': None}
+ data = {'id': 4, 'name': 'source-4', 'target': None}
serializer = NullableForeignKeySourceSerializer(data=data)
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, data)
- self.assertEqual(obj.name, u'source-4')
+ self.assertEqual(obj.name, 'source-4')
# Ensure source 4 is created, and everything else is as expected
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': 'target-1'},
- {'id': 2, 'name': u'source-2', 'target': 'target-1'},
- {'id': 3, 'name': u'source-3', 'target': None},
- {'id': 4, 'name': u'source-4', 'target': None}
+ {'id': 1, 'name': 'source-1', 'target': 'target-1'},
+ {'id': 2, 'name': 'source-2', 'target': 'target-1'},
+ {'id': 3, 'name': 'source-3', 'target': None},
+ {'id': 4, 'name': 'source-4', 'target': None}
]
self.assertEquals(serializer.data, expected)
@@ -196,27 +196,27 @@ class SlugNullableForeignKeyTests(TestCase):
The emptystring should be interpreted as null in the context
of relationships.
"""
- data = {'id': 4, 'name': u'source-4', 'target': ''}
- expected_data = {'id': 4, 'name': u'source-4', 'target': None}
+ data = {'id': 4, 'name': 'source-4', 'target': ''}
+ expected_data = {'id': 4, 'name': 'source-4', 'target': None}
serializer = NullableForeignKeySourceSerializer(data=data)
self.assertTrue(serializer.is_valid())
obj = serializer.save()
self.assertEquals(serializer.data, expected_data)
- self.assertEqual(obj.name, u'source-4')
+ self.assertEqual(obj.name, 'source-4')
# Ensure source 4 is created, and everything else is as expected
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': 'target-1'},
- {'id': 2, 'name': u'source-2', 'target': 'target-1'},
- {'id': 3, 'name': u'source-3', 'target': None},
- {'id': 4, 'name': u'source-4', 'target': None}
+ {'id': 1, 'name': 'source-1', 'target': 'target-1'},
+ {'id': 2, 'name': 'source-2', 'target': 'target-1'},
+ {'id': 3, 'name': 'source-3', 'target': None},
+ {'id': 4, 'name': 'source-4', 'target': None}
]
self.assertEquals(serializer.data, expected)
def test_foreign_key_update_with_valid_null(self):
- data = {'id': 1, 'name': u'source-1', 'target': None}
+ data = {'id': 1, 'name': 'source-1', 'target': None}
instance = NullableForeignKeySource.objects.get(pk=1)
serializer = NullableForeignKeySourceSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -227,9 +227,9 @@ class SlugNullableForeignKeyTests(TestCase):
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': None},
- {'id': 2, 'name': u'source-2', 'target': 'target-1'},
- {'id': 3, 'name': u'source-3', 'target': None}
+ {'id': 1, 'name': 'source-1', 'target': None},
+ {'id': 2, 'name': 'source-2', 'target': 'target-1'},
+ {'id': 3, 'name': 'source-3', 'target': None}
]
self.assertEquals(serializer.data, expected)
@@ -238,8 +238,8 @@ class SlugNullableForeignKeyTests(TestCase):
The emptystring should be interpreted as null in the context
of relationships.
"""
- data = {'id': 1, 'name': u'source-1', 'target': ''}
- expected_data = {'id': 1, 'name': u'source-1', 'target': None}
+ data = {'id': 1, 'name': 'source-1', 'target': ''}
+ expected_data = {'id': 1, 'name': 'source-1', 'target': None}
instance = NullableForeignKeySource.objects.get(pk=1)
serializer = NullableForeignKeySourceSerializer(instance, data=data)
self.assertTrue(serializer.is_valid())
@@ -250,8 +250,8 @@ class SlugNullableForeignKeyTests(TestCase):
queryset = NullableForeignKeySource.objects.all()
serializer = NullableForeignKeySourceSerializer(queryset)
expected = [
- {'id': 1, 'name': u'source-1', 'target': None},
- {'id': 2, 'name': u'source-2', 'target': 'target-1'},
- {'id': 3, 'name': u'source-3', 'target': None}
+ {'id': 1, 'name': 'source-1', 'target': None},
+ {'id': 2, 'name': 'source-2', 'target': 'target-1'},
+ {'id': 3, 'name': 'source-3', 'target': None}
]
self.assertEquals(serializer.data, expected)
diff --git a/rest_framework/tests/renderers.py b/rest_framework/tests/renderers.py
index c1b4e624..72405336 100644
--- a/rest_framework/tests/renderers.py
+++ b/rest_framework/tests/renderers.py
@@ -14,7 +14,8 @@ from rest_framework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \
from rest_framework.parsers import YAMLParser, XMLParser
from rest_framework.settings import api_settings
-from StringIO import StringIO
+from rest_framework.compat import StringIO
+from rest_framework.compat import six
import datetime
from decimal import Decimal
@@ -22,8 +23,8 @@ from decimal import Decimal
DUMMYSTATUS = status.HTTP_200_OK
DUMMYCONTENT = 'dummycontent'
-RENDERER_A_SERIALIZER = lambda x: 'Renderer A: %s' % x
-RENDERER_B_SERIALIZER = lambda x: 'Renderer B: %s' % x
+RENDERER_A_SERIALIZER = lambda x: ('Renderer A: %s' % x).encode('ascii')
+RENDERER_B_SERIALIZER = lambda x: ('Renderer B: %s' % x).encode('ascii')
expected_results = [
@@ -140,7 +141,7 @@ class RendererEndToEndTests(TestCase):
resp = self.client.head('/')
self.assertEquals(resp.status_code, DUMMYSTATUS)
self.assertEquals(resp['Content-Type'], RendererA.media_type)
- self.assertEquals(resp.content, '')
+ self.assertEquals(resp.content, six.b(''))
def test_default_renderer_serializes_content_on_accept_any(self):
"""If the Accept header is set to */* the default renderer should serialize the response."""
@@ -267,7 +268,8 @@ class JSONPRendererTests(TestCase):
HTTP_ACCEPT='application/javascript')
self.assertEquals(resp.status_code, 200)
self.assertEquals(resp['Content-Type'], 'application/javascript')
- self.assertEquals(resp.content, 'callback(%s);' % _flat_repr)
+ self.assertEquals(resp.content,
+ ('callback(%s);' % _flat_repr).encode('ascii'))
def test_without_callback_without_json_renderer(self):
"""
@@ -277,7 +279,8 @@ class JSONPRendererTests(TestCase):
HTTP_ACCEPT='application/javascript')
self.assertEquals(resp.status_code, 200)
self.assertEquals(resp['Content-Type'], 'application/javascript')
- self.assertEquals(resp.content, 'callback(%s);' % _flat_repr)
+ self.assertEquals(resp.content,
+ ('callback(%s);' % _flat_repr).encode('ascii'))
def test_with_callback(self):
"""
@@ -288,7 +291,8 @@ class JSONPRendererTests(TestCase):
HTTP_ACCEPT='application/javascript')
self.assertEquals(resp.status_code, 200)
self.assertEquals(resp['Content-Type'], 'application/javascript')
- self.assertEquals(resp.content, '%s(%s);' % (callback_func, _flat_repr))
+ self.assertEquals(resp.content,
+ ('%s(%s);' % (callback_func, _flat_repr)).encode('ascii'))
if yaml:
diff --git a/rest_framework/tests/request.py b/rest_framework/tests/request.py
index 4b032405..92b1bfd8 100644
--- a/rest_framework/tests/request.py
+++ b/rest_framework/tests/request.py
@@ -20,6 +20,7 @@ from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.views import APIView
+from rest_framework.compat import six
factory = RequestFactory()
@@ -79,14 +80,14 @@ class TestContentParsing(TestCase):
data = {'qwerty': 'uiop'}
request = Request(factory.post('/', data))
request.parsers = (FormParser(), MultiPartParser())
- self.assertEqual(request.DATA.items(), data.items())
+ self.assertEqual(list(request.DATA.items()), list(data.items()))
def test_request_DATA_with_text_content(self):
"""
Ensure request.DATA returns content for POST request with
non-form content.
"""
- content = 'qwerty'
+ content = six.b('qwerty')
content_type = 'text/plain'
request = Request(factory.post('/', content, content_type=content_type))
request.parsers = (PlainTextParser(),)
@@ -99,7 +100,7 @@ class TestContentParsing(TestCase):
data = {'qwerty': 'uiop'}
request = Request(factory.post('/', data))
request.parsers = (FormParser(), MultiPartParser())
- self.assertEqual(request.POST.items(), data.items())
+ self.assertEqual(list(request.POST.items()), list(data.items()))
def test_standard_behaviour_determines_form_content_PUT(self):
"""
@@ -117,14 +118,14 @@ class TestContentParsing(TestCase):
request = Request(factory.put('/', data))
request.parsers = (FormParser(), MultiPartParser())
- self.assertEqual(request.DATA.items(), data.items())
+ self.assertEqual(list(request.DATA.items()), list(data.items()))
def test_standard_behaviour_determines_non_form_content_PUT(self):
"""
Ensure request.DATA returns content for PUT request with
non-form content.
"""
- content = 'qwerty'
+ content = six.b('qwerty')
content_type = 'text/plain'
request = Request(factory.put('/', content, content_type=content_type))
request.parsers = (PlainTextParser(), )
diff --git a/rest_framework/tests/response.py b/rest_framework/tests/response.py
index 875f4d42..453488d0 100644
--- a/rest_framework/tests/response.py
+++ b/rest_framework/tests/response.py
@@ -9,6 +9,7 @@ from rest_framework.renderers import (
BrowsableAPIRenderer
)
from rest_framework.settings import api_settings
+from rest_framework.compat import six
class MockPickleRenderer(BaseRenderer):
@@ -22,8 +23,8 @@ class MockJsonRenderer(BaseRenderer):
DUMMYSTATUS = status.HTTP_200_OK
DUMMYCONTENT = 'dummycontent'
-RENDERER_A_SERIALIZER = lambda x: 'Renderer A: %s' % x
-RENDERER_B_SERIALIZER = lambda x: 'Renderer B: %s' % x
+RENDERER_A_SERIALIZER = lambda x: ('Renderer A: %s' % x).encode('ascii')
+RENDERER_B_SERIALIZER = lambda x: ('Renderer B: %s' % x).encode('ascii')
class RendererA(BaseRenderer):
@@ -92,7 +93,7 @@ class RendererIntegrationTests(TestCase):
resp = self.client.head('/')
self.assertEquals(resp.status_code, DUMMYSTATUS)
self.assertEquals(resp['Content-Type'], RendererA.media_type)
- self.assertEquals(resp.content, '')
+ self.assertEquals(resp.content, six.b(''))
def test_default_renderer_serializes_content_on_accept_any(self):
"""If the Accept header is set to */* the default renderer should serialize the response."""
diff --git a/rest_framework/tests/serializer.py b/rest_framework/tests/serializer.py
index 2cbd0c1a..1f46cfc7 100644
--- a/rest_framework/tests/serializer.py
+++ b/rest_framework/tests/serializer.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
import datetime
import pickle
from django.utils.datastructures import MultiValueDict
@@ -201,12 +203,12 @@ class ValidationTests(TestCase):
def test_create(self):
serializer = CommentSerializer(data=self.data)
self.assertEquals(serializer.is_valid(), False)
- self.assertEquals(serializer.errors, {'content': [u'Ensure this value has at most 1000 characters (it has 1001).']})
+ self.assertEquals(serializer.errors, {'content': ['Ensure this value has at most 1000 characters (it has 1001).']})
def test_update(self):
serializer = CommentSerializer(self.comment, data=self.data)
self.assertEquals(serializer.is_valid(), False)
- self.assertEquals(serializer.errors, {'content': [u'Ensure this value has at most 1000 characters (it has 1001).']})
+ self.assertEquals(serializer.errors, {'content': ['Ensure this value has at most 1000 characters (it has 1001).']})
def test_update_missing_field(self):
data = {
@@ -215,7 +217,7 @@ class ValidationTests(TestCase):
}
serializer = CommentSerializer(self.comment, data=data)
self.assertEquals(serializer.is_valid(), False)
- self.assertEquals(serializer.errors, {'email': [u'This field is required.']})
+ self.assertEquals(serializer.errors, {'email': ['This field is required.']})
def test_missing_bool_with_default(self):
"""Make sure that a boolean value with a 'False' value is not
@@ -235,17 +237,17 @@ class ValidationTests(TestCase):
data = ['i am', 'a', 'list']
serializer = CommentSerializer(self.comment, data=data)
self.assertEquals(serializer.is_valid(), False)
- self.assertEquals(serializer.errors, {'non_field_errors': [u'Invalid data']})
+ self.assertEquals(serializer.errors, {'non_field_errors': ['Invalid data']})
data = 'and i am a string'
serializer = CommentSerializer(self.comment, data=data)
self.assertEquals(serializer.is_valid(), False)
- self.assertEquals(serializer.errors, {'non_field_errors': [u'Invalid data']})
+ self.assertEquals(serializer.errors, {'non_field_errors': ['Invalid data']})
data = 42
serializer = CommentSerializer(self.comment, data=data)
self.assertEquals(serializer.is_valid(), False)
- self.assertEquals(serializer.errors, {'non_field_errors': [u'Invalid data']})
+ self.assertEquals(serializer.errors, {'non_field_errors': ['Invalid data']})
def test_cross_field_validation(self):
@@ -269,7 +271,7 @@ class ValidationTests(TestCase):
serializer = CommentSerializerWithCrossFieldValidator(data=data)
self.assertFalse(serializer.is_valid())
- self.assertEquals(serializer.errors, {'non_field_errors': [u'Email address not in content']})
+ self.assertEquals(serializer.errors, {'non_field_errors': ['Email address not in content']})
def test_null_is_true_fields(self):
"""
@@ -285,7 +287,7 @@ class ValidationTests(TestCase):
}
serializer = ActionItemSerializer(data=data)
self.assertEquals(serializer.is_valid(), False)
- self.assertEquals(serializer.errors, {'title': [u'Ensure this value has at most 200 characters (it has 201).']})
+ self.assertEquals(serializer.errors, {'title': ['Ensure this value has at most 200 characters (it has 201).']})
def test_modelserializer_max_length_exceeded_with_custom_restore(self):
"""
@@ -299,7 +301,7 @@ class ValidationTests(TestCase):
}
serializer = ActionItemSerializerCustomRestore(data=data)
self.assertEquals(serializer.is_valid(), False)
- self.assertEquals(serializer.errors, {'title': [u'Ensure this value has at most 200 characters (it has 201).']})
+ self.assertEquals(serializer.errors, {'title': ['Ensure this value has at most 200 characters (it has 201).']})
def test_default_modelfield_max_length_exceeded(self):
data = {
@@ -308,7 +310,7 @@ class ValidationTests(TestCase):
}
serializer = ActionItemSerializer(data=data)
self.assertEquals(serializer.is_valid(), False)
- self.assertEquals(serializer.errors, {'info': [u'Ensure this value has at most 12 characters (it has 13).']})
+ self.assertEquals(serializer.errors, {'info': ['Ensure this value has at most 12 characters (it has 13).']})
class CustomValidationTests(TestCase):
@@ -339,7 +341,7 @@ class CustomValidationTests(TestCase):
serializer = self.CommentSerializerWithFieldValidator(data=data)
self.assertFalse(serializer.is_valid())
- self.assertEquals(serializer.errors, {'content': [u'Test not in value']})
+ self.assertEquals(serializer.errors, {'content': ['Test not in value']})
def test_missing_data(self):
"""
@@ -351,7 +353,7 @@ class CustomValidationTests(TestCase):
}
serializer = self.CommentSerializerWithFieldValidator(data=incomplete_data)
self.assertFalse(serializer.is_valid())
- self.assertEquals(serializer.errors, {'content': [u'This field is required.']})
+ self.assertEquals(serializer.errors, {'content': ['This field is required.']})
def test_wrong_data(self):
"""
@@ -364,7 +366,7 @@ class CustomValidationTests(TestCase):
}
serializer = self.CommentSerializerWithFieldValidator(data=wrong_data)
self.assertFalse(serializer.is_valid())
- self.assertEquals(serializer.errors, {'email': [u'Enter a valid e-mail address.']})
+ self.assertEquals(serializer.errors, {'email': ['Enter a valid e-mail address.']})
class PositiveIntegerAsChoiceTests(TestCase):
@@ -384,7 +386,7 @@ class ModelValidationTests(TestCase):
serializer.save()
second_serializer = AlbumsSerializer(data={'title': 'a'})
self.assertFalse(second_serializer.is_valid())
- self.assertEqual(second_serializer.errors, {'title': [u'Album with this Title already exists.']})
+ self.assertEqual(second_serializer.errors, {'title': ['Album with this Title already exists.']})
def test_foreign_key_with_partial(self):
"""
@@ -422,15 +424,15 @@ class RegexValidationTest(TestCase):
def test_create_failed(self):
serializer = BookSerializer(data={'isbn': '1234567890'})
self.assertFalse(serializer.is_valid())
- self.assertEquals(serializer.errors, {'isbn': [u'isbn has to be exact 13 numbers']})
+ self.assertEquals(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']})
serializer = BookSerializer(data={'isbn': '12345678901234'})
self.assertFalse(serializer.is_valid())
- self.assertEquals(serializer.errors, {'isbn': [u'isbn has to be exact 13 numbers']})
+ self.assertEquals(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']})
serializer = BookSerializer(data={'isbn': 'abcdefghijklm'})
self.assertFalse(serializer.is_valid())
- self.assertEquals(serializer.errors, {'isbn': [u'isbn has to be exact 13 numbers']})
+ self.assertEquals(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']})
def test_create_success(self):
serializer = BookSerializer(data={'isbn': '1234567890123'})
@@ -745,11 +747,11 @@ class RelatedTraversalTest(TestCase):
serializer = BlogPostSerializer(instance=post)
expected = {
- 'title': u'Test blog post',
+ 'title': 'Test blog post',
'comments': [{
- 'text': u'I love this blog post',
+ 'text': 'I love this blog post',
'post_owner': {
- "name": u"django",
+ "name": "django",
"age": None
}
}]
@@ -784,8 +786,8 @@ class SerializerMethodFieldTests(TestCase):
serializer = self.serializer_class(source_data)
expected = {
- 'beep': u'hello!',
- 'boop': [u'a', u'b', u'c'],
+ 'beep': 'hello!',
+ 'boop': ['a', 'b', 'c'],
'boop_count': 3,
}
@@ -884,8 +886,8 @@ class DepthTest(TestCase):
depth = 1
serializer = BlogPostSerializer(instance=post)
- expected = {'id': 1, 'title': u'Test blog post',
- 'writer': {'id': 1, 'name': u'django', 'age': 1}}
+ expected = {'id': 1, 'title': 'Test blog post',
+ 'writer': {'id': 1, 'name': 'django', 'age': 1}}
self.assertEqual(serializer.data, expected)
@@ -904,8 +906,8 @@ class DepthTest(TestCase):
model = BlogPost
serializer = BlogPostSerializer(instance=post)
- expected = {'id': 1, 'title': u'Test blog post',
- 'writer': {'id': 1, 'name': u'django', 'age': 1}}
+ expected = {'id': 1, 'title': 'Test blog post',
+ 'writer': {'id': 1, 'name': 'django', 'age': 1}}
self.assertEqual(serializer.data, expected)
diff --git a/rest_framework/tests/utils.py b/rest_framework/tests/utils.py
index 3906adb9..4e6faac4 100644
--- a/rest_framework/tests/utils.py
+++ b/rest_framework/tests/utils.py
@@ -1,6 +1,6 @@
from django.test.client import RequestFactory, FakePayload
from django.test.client import MULTIPART_CONTENT
-from urlparse import urlparse
+from rest_framework.compat import urlparse
class RequestFactory(RequestFactory):
@@ -14,7 +14,7 @@ class RequestFactory(RequestFactory):
patch_data = self._encode_data(data, content_type)
- parsed = urlparse(path)
+ parsed = urlparse.urlparse(path)
r = {
'CONTENT_LENGTH': len(patch_data),
'CONTENT_TYPE': content_type,
diff --git a/rest_framework/tests/validators.py b/rest_framework/tests/validators.py
index c032985e..8844cb74 100644
--- a/rest_framework/tests/validators.py
+++ b/rest_framework/tests/validators.py
@@ -139,7 +139,7 @@
# raise errors on unexpected request data"""
# content = {'qwerty': 'uiop', 'extra': 'extra'}
# validator.allow_unknown_form_fields = True
-# self.assertEqual({'qwerty': u'uiop'},
+# self.assertEqual({'qwerty': 'uiop'},
# validator.validate_request(content, None),
# "Resource didn't accept unknown fields.")
# validator.allow_unknown_form_fields = False
diff --git a/rest_framework/tests/views.py b/rest_framework/tests/views.py
index 7cd82656..f2432516 100644
--- a/rest_framework/tests/views.py
+++ b/rest_framework/tests/views.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
import copy
from django.test import TestCase
from django.test.client import RequestFactory
@@ -49,7 +51,7 @@ class ClassBasedViewIntegrationTests(TestCase):
request = factory.post('/', 'f00bar', content_type='application/json')
response = self.view(request)
expected = {
- 'detail': u'JSON parse error - No JSON object could be decoded'
+ 'detail': 'JSON parse error - No JSON object could be decoded'
}
self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEquals(sanitise_json_error(response.data), expected)
@@ -64,7 +66,7 @@ class ClassBasedViewIntegrationTests(TestCase):
request = factory.post('/', form_data)
response = self.view(request)
expected = {
- 'detail': u'JSON parse error - No JSON object could be decoded'
+ 'detail': 'JSON parse error - No JSON object could be decoded'
}
self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEquals(sanitise_json_error(response.data), expected)
@@ -78,7 +80,7 @@ class FunctionBasedViewIntegrationTests(TestCase):
request = factory.post('/', 'f00bar', content_type='application/json')
response = self.view(request)
expected = {
- 'detail': u'JSON parse error - No JSON object could be decoded'
+ 'detail': 'JSON parse error - No JSON object could be decoded'
}
self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEquals(sanitise_json_error(response.data), expected)
@@ -93,7 +95,7 @@ class FunctionBasedViewIntegrationTests(TestCase):
request = factory.post('/', form_data)
response = self.view(request)
expected = {
- 'detail': u'JSON parse error - No JSON object could be decoded'
+ 'detail': 'JSON parse error - No JSON object could be decoded'
}
self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEquals(sanitise_json_error(response.data), expected)
diff --git a/rest_framework/utils/__init__.py b/rest_framework/utils/__init__.py
index 84fcb5db..1603f972 100644
--- a/rest_framework/utils/__init__.py
+++ b/rest_framework/utils/__init__.py
@@ -1,6 +1,7 @@
-from django.utils.encoding import smart_unicode
from django.utils.xmlutils import SimplerXMLGenerator
from rest_framework.compat import StringIO
+from rest_framework.compat import six
+from rest_framework.compat import smart_text
import re
import xml.etree.ElementTree as ET
@@ -70,7 +71,7 @@ class XMLRenderer():
xml.endElement("list-item")
elif isinstance(data, dict):
- for key, value in data.iteritems():
+ for key, value in six.iteritems(data):
xml.startElement(key, {})
self._to_xml(xml, value)
xml.endElement(key)
@@ -80,10 +81,10 @@ class XMLRenderer():
pass
else:
- xml.characters(smart_unicode(data))
+ xml.characters(smart_text(data))
def dict2xml(self, data):
- stream = StringIO.StringIO()
+ stream = StringIO()
xml = SimplerXMLGenerator(stream, "utf-8")
xml.startDocument()
diff --git a/rest_framework/utils/mediatypes.py b/rest_framework/utils/mediatypes.py
index ee7f3a54..aea1b629 100644
--- a/rest_framework/utils/mediatypes.py
+++ b/rest_framework/utils/mediatypes.py
@@ -5,6 +5,7 @@ See http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
"""
from django.http.multipartparser import parse_header
+from rest_framework import HTTP_HEADER_ENCODING
def media_type_matches(lhs, rhs):
@@ -47,7 +48,7 @@ class _MediaType(object):
if media_type_str is None:
media_type_str = ''
self.orig = media_type_str
- self.full_type, self.params = parse_header(media_type_str)
+ self.full_type, self.params = parse_header(media_type_str.encode(HTTP_HEADER_ENCODING))
self.main_type, sep, self.sub_type = self.full_type.partition('/')
def match(self, other):
diff --git a/setup.py b/setup.py
index 26d07283..640bac4d 100755
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
+#from __future__ import unicode_literals
+
from setuptools import setup
import re
import os
@@ -45,9 +47,9 @@ version = get_version('rest_framework')
if sys.argv[-1] == 'publish':
os.system("python setup.py sdist upload")
- print "You probably want to also tag the version now:"
- print " git tag -a %s -m 'version %s'" % (version, version)
- print " git push --tags"
+ print("You probably want to also tag the version now:")
+ print(" git tag -a %s -m 'version %s'" % (version, version))
+ print(" git push --tags")
sys.exit()
@@ -72,6 +74,7 @@ setup(
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
+ 'Programming Language :: Python :: 3',
'Topic :: Internet :: WWW/HTTP',
]
)
diff --git a/tox.ini b/tox.ini
index 22c85e49..05733efe 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,28 +1,33 @@
[tox]
downloadcache = {toxworkdir}/cache/
-envlist = py2.7-django1.5,py2.7-django1.4,py2.7-django1.3,py2.6-django1.5,py2.6-django1.4,py2.6-django1.3
+envlist = py3.3-django1.5,py3.2-django1.5,py2.7-django1.5,py2.7-django1.4,py2.7-django1.3,py2.6-django1.5,py2.6-django1.4,py2.6-django1.3
[testenv]
commands = {envpython} rest_framework/runtests/runtests.py
-[testenv:py2.7-django1.5]
-basepython = python2.7
-deps = https://github.com/django/django/zipball/master
- django-filter==0.5.4
+[testenv:py3.3-django1.5]
+basepython = python3.3
+deps = https://www.djangoproject.com/download/1.5c1/tarball/
+ https://github.com/alex/django-filter/archive/master.tar.gz
-[testenv:py2.7-django1.4]
-basepython = python2.7
-deps = django==1.4.3
- django-filter==0.5.4
+[testenv:py3.2-django1.5]
+basepython = python3.2
+deps = https://www.djangoproject.com/download/1.5c1/tarball/
+ https://github.com/alex/django-filter/archive/master.tar.gz
-[testenv:py2.7-django1.3]
+[testenv:py2.7-django1.5]
basepython = python2.7
-deps = django==1.3.5
+deps = https://www.djangoproject.com/download/1.5c1/tarball/
django-filter==0.5.4
[testenv:py2.6-django1.5]
basepython = python2.6
-deps = https://github.com/django/django/zipball/master
+deps = https://www.djangoproject.com/download/1.5c1/tarball/
+ django-filter==0.5.4
+
+[testenv:py2.7-django1.4]
+basepython = python2.7
+deps = django==1.4.3
django-filter==0.5.4
[testenv:py2.6-django1.4]
@@ -30,6 +35,11 @@ basepython = python2.6
deps = django==1.4.3
django-filter==0.5.4
+[testenv:py2.7-django1.3]
+basepython = python2.7
+deps = django==1.3.5
+ django-filter==0.5.4
+
[testenv:py2.6-django1.3]
basepython = python2.6
deps = django==1.3.5