aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml9
-rw-r--r--docs/api-guide/fields.md4
-rw-r--r--docs/api-guide/filtering.md2
-rw-r--r--docs/api-guide/generic-views.md4
-rw-r--r--docs/api-guide/relations.md2
-rw-r--r--docs/api-guide/serializers.md2
-rw-r--r--docs/topics/release-notes.md41
-rw-r--r--requirements/requirements-codestyle.txt4
-rw-r--r--rest_framework/__init__.py2
-rw-r--r--rest_framework/authtoken/views.py3
-rw-r--r--rest_framework/fields.py2
-rw-r--r--rest_framework/locale/fr/LC_MESSAGES/django.mobin6975 -> 9198 bytes
-rw-r--r--rest_framework/locale/fr/LC_MESSAGES/django.po46
-rw-r--r--rest_framework/locale/pt_PT/LC_MESSAGES/django.mobin0 -> 509 bytes
-rw-r--r--rest_framework/locale/pt_PT/LC_MESSAGES/django.po324
-rw-r--r--rest_framework/locale/vi/LC_MESSAGES/django.mobin0 -> 485 bytes
-rw-r--r--rest_framework/locale/vi/LC_MESSAGES/django.po324
-rw-r--r--rest_framework/metadata.py1
-rw-r--r--rest_framework/renderers.py28
-rw-r--r--rest_framework/response.py4
-rw-r--r--rest_framework/reverse.py9
-rw-r--r--rest_framework/static/rest_framework/js/default.js4
-rw-r--r--tests/test_metadata.py7
-rw-r--r--tests/test_reverse.py27
-rw-r--r--tests/test_versioning.py44
-rw-r--r--tox.ini6
26 files changed, 847 insertions, 52 deletions
diff --git a/.travis.yml b/.travis.yml
index 3eb89dc4..71cec7be 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,6 +5,10 @@ sudo: false
env:
- TOX_ENV=py27-flake8
- TOX_ENV=py27-docs
+ - TOX_ENV=py34-django18
+ - TOX_ENV=py33-django18
+ - TOX_ENV=py32-django18
+ - TOX_ENV=py27-django18
- TOX_ENV=py34-django17
- TOX_ENV=py33-django17
- TOX_ENV=py32-django17
@@ -21,10 +25,7 @@ env:
- TOX_ENV=py26-django15
- TOX_ENV=py27-django14
- TOX_ENV=py26-django14
- - TOX_ENV=py34-django18beta
- - TOX_ENV=py33-django18beta
- - TOX_ENV=py32-django18beta
- - TOX_ENV=py27-django18beta
+
install:
- pip install tox
diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md
index 5edc997a..c87db785 100644
--- a/docs/api-guide/fields.md
+++ b/docs/api-guide/fields.md
@@ -434,7 +434,7 @@ A field class that does not take a value based on user input, but instead takes
For example, to include a field that always provides the current time as part of the serializer validated data, you would use the following:
- modified = serializer.HiddenField(default=timezone.now)
+ modified = serializers.HiddenField(default=timezone.now)
The `HiddenField` class is usually only needed if you have some validation that needs to run based on some pre-provided field values, but you do not want to expose all of those fields to the end user.
@@ -481,7 +481,7 @@ If you want to create a custom field, you'll need to subclass `Field` and then o
The `.to_representation()` method is called to convert the initial datatype into a primitive, serializable datatype.
-The `to_internal_value()` method is called to restore a primitive datatype into its internal python representation. This method should raise a `serializer.ValidationError` if the data is invalid.
+The `to_internal_value()` method is called to restore a primitive datatype into its internal python representation. This method should raise a `serializers.ValidationError` if the data is invalid.
Note that the `WritableField` class that was present in version 2.x no longer exists. You should subclass `Field` and override `to_internal_value()` if the field supports data input.
diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md
index b16b6be5..50c3f2cf 100644
--- a/docs/api-guide/filtering.md
+++ b/docs/api-guide/filtering.md
@@ -72,7 +72,7 @@ We can override `.get_queryset()` to deal with URLs such as `http://example.com/
by filtering against a `username` query parameter in the URL.
"""
queryset = Purchase.objects.all()
- username = self.request.QUERY_PARAMS.get('username', None)
+ username = self.request.query_params.get('username', None)
if username is not None:
queryset = queryset.filter(purchaser__username=username)
return queryset
diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md
index 7df3d6ff..ccf84592 100644
--- a/docs/api-guide/generic-views.md
+++ b/docs/api-guide/generic-views.md
@@ -133,9 +133,9 @@ May be overridden to provide more complex behavior with filters, such as using d
For example:
def get_filter_backends(self):
- if "geo_route" in self.request.QUERY_PARAMS:
+ if "geo_route" in self.request.query_params:
return (GeoRouteFilter, CategoryFilter)
- elif "geo_point" in self.request.QUERY_PARAMS:
+ elif "geo_point" in self.request.query_params:
return (GeoPointFilter, CategoryFilter)
return (CategoryFilter,)
diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md
index 093bbdd0..31d59e1f 100644
--- a/docs/api-guide/relations.md
+++ b/docs/api-guide/relations.md
@@ -46,7 +46,7 @@ In order to explain the various types of relational fields, we'll use a couple o
class Meta:
unique_together = ('album', 'order')
- order_by = 'order'
+ ordering = ['order']
def __unicode__(self):
return '%d: %s' % (self.order, self.title)
diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md
index aad2236f..d9ded52f 100644
--- a/docs/api-guide/serializers.md
+++ b/docs/api-guide/serializers.md
@@ -344,7 +344,7 @@ Here's an example for an `update()` method on our previous `UserSerializer` clas
return instance
-Because the behavior of nested creates and updates can be ambiguous, and may require complex dependancies between related models, REST framework 3 requires you to always write these methods explicitly. The default `ModelSerializer` `.create()` and `.update()` methods do not include support for writable nested representations.
+Because the behavior of nested creates and updates can be ambiguous, and may require complex dependencies between related models, REST framework 3 requires you to always write these methods explicitly. The default `ModelSerializer` `.create()` and `.update()` methods do not include support for writable nested representations.
It is possible that a third party package, providing automatic support some kinds of automatic writable nested representations may be released alongside the 3.1 release.
diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md
index 84d310c2..270a3360 100644
--- a/docs/topics/release-notes.md
+++ b/docs/topics/release-notes.md
@@ -38,7 +38,24 @@ You can determine your currently installed version using `pip freeze`:
---
-## 3.0.x series
+## 3.1.x series
+
+### 3.1.1
+
+**Date**: [23rd March 2015][3.1.1-milestone].
+
+* **Security fix**: Escape tab switching cookie name in browsable API.
+* Display input forms in browsable API if `serializer_class` is used, even when `get_serializer` method does not exist on the view. ([#2743](gh2743))
+* Use a password input for the AuthTokenSerializer. ([#2741](gh2741))
+* Fix missing anchor closing tag after next button. ([#2691][gh2691])
+* Fix `lookup_url_kwarg` handling in viewsets. ([#2685][gh2685], [#2591][gh2591])
+* Fix problem with importing `rest_framework.views` in `apps.py` ([#2678][gh2678])
+* LimitOffsetPagination raises `TypeError` if PAGE_SIZE not set ([#2667][gh2667], [#2700][gh2700])
+* German translation for `min_value` field error message references `max_value`. ([#2645][gh2645])
+* Remove `MergeDict`. ([#2640][gh2640])
+* Support serializing unsaved models with related fields. ([#2637][gh2637], [#2641][gh2641])
+* Allow blank/null on radio.html choices. ([#2631][gh2631])
+
### 3.1.0
@@ -46,6 +63,10 @@ You can determine your currently installed version using `pip freeze`:
For full details see the [3.1 release announcement](3.1-announcement.md).
+---
+
+## 3.0.x series
+
### 3.0.5
**Date**: [10th February 2015][3.0.5-milestone].
@@ -142,7 +163,7 @@ For full details see the [3.0 release announcement](3.0-announcement.md).
---
-For older release notes, [please see the version 2.x documentation](old-release-notes).
+For older release notes, [please see the version 2.x documentation][old-release-notes].
[cite]: http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/ar01s04.html
[deprecation-policy]: #deprecation-policy
@@ -161,6 +182,8 @@ For older release notes, [please see the version 2.x documentation](old-release-
[3.0.3-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.3+Release%22
[3.0.4-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.4+Release%22
[3.0.5-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.5+Release%22
+[3.1.0-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.1.0+Release%22
+[3.1.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.1.1+Release%22
<!-- 3.0.1 -->
[gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013
@@ -259,3 +282,17 @@ For older release notes, [please see the version 2.x documentation](old-release-
[gh2519]: https://github.com/tomchristie/django-rest-framework/issues/2519
[gh2524]: https://github.com/tomchristie/django-rest-framework/issues/2524
[gh2530]: https://github.com/tomchristie/django-rest-framework/issues/2530
+<!-- 3.1.1 -->
+[gh2691]: https://github.com/tomchristie/django-rest-framework/issues/2691
+[gh2685]: https://github.com/tomchristie/django-rest-framework/issues/2685
+[gh2591]: https://github.com/tomchristie/django-rest-framework/issues/2591
+[gh2678]: https://github.com/tomchristie/django-rest-framework/issues/2678
+[gh2667]: https://github.com/tomchristie/django-rest-framework/issues/2667
+[gh2700]: https://github.com/tomchristie/django-rest-framework/issues/2700
+[gh2645]: https://github.com/tomchristie/django-rest-framework/issues/2645
+[gh2640]: https://github.com/tomchristie/django-rest-framework/issues/2640
+[gh2637]: https://github.com/tomchristie/django-rest-framework/issues/2637
+[gh2641]: https://github.com/tomchristie/django-rest-framework/issues/2641
+[gh2631]: https://github.com/tomchristie/django-rest-framework/issues/2631
+[gh2741]: https://github.com/tomchristie/django-rest-framework/issues/2641
+[gh2743]: https://github.com/tomchristie/django-rest-framework/issues/2643
diff --git a/requirements/requirements-codestyle.txt b/requirements/requirements-codestyle.txt
index 4e2be24c..88f61fdf 100644
--- a/requirements/requirements-codestyle.txt
+++ b/requirements/requirements-codestyle.txt
@@ -1,3 +1,3 @@
# PEP8 code linting, which we run on all commits.
-flake8==2.3.0
-pep8==1.6.2
+flake8==2.4.0
+pep8==1.5.7
diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py
index f8bbeee3..62bdd2c5 100644
--- a/rest_framework/__init__.py
+++ b/rest_framework/__init__.py
@@ -8,7 +8,7 @@ ______ _____ _____ _____ __
"""
__title__ = 'Django REST framework'
-__version__ = '3.1.0'
+__version__ = '3.1.1'
__author__ = 'Tom Christie'
__license__ = 'BSD 2-Clause'
__copyright__ = 'Copyright 2011-2015 Tom Christie'
diff --git a/rest_framework/authtoken/views.py b/rest_framework/authtoken/views.py
index b75c2e25..66bbc49b 100644
--- a/rest_framework/authtoken/views.py
+++ b/rest_framework/authtoken/views.py
@@ -11,9 +11,10 @@ class ObtainAuthToken(APIView):
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
+ serializer_class = AuthTokenSerializer
def post(self, request):
- serializer = AuthTokenSerializer(data=request.data)
+ serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index a80862e8..bea77300 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -339,7 +339,7 @@ class Field(object):
* Raise `ValidationError`, indicating invalid data.
* Raise `SkipField`, indicating that the field should be ignored.
* Return (True, data), indicating an empty value that should be
- returned without any furhter validation being applied.
+ returned without any further validation being applied.
* Return (False, data), indicating a non-empty value, that should
have validation applied as normal.
"""
diff --git a/rest_framework/locale/fr/LC_MESSAGES/django.mo b/rest_framework/locale/fr/LC_MESSAGES/django.mo
index 68519d45..0b5224a5 100644
--- a/rest_framework/locale/fr/LC_MESSAGES/django.mo
+++ b/rest_framework/locale/fr/LC_MESSAGES/django.mo
Binary files differ
diff --git a/rest_framework/locale/fr/LC_MESSAGES/django.po b/rest_framework/locale/fr/LC_MESSAGES/django.po
index e8597c30..a942af6d 100644
--- a/rest_framework/locale/fr/LC_MESSAGES/django.po
+++ b/rest_framework/locale/fr/LC_MESSAGES/django.po
@@ -5,13 +5,15 @@
# Translators:
# Etienne Desgagné <etienne.desgagne@evimbec.ca>, 2015
# Martin Maillard <martin.maillard@gmail.com>, 2015
+# Martin Maillard <martin.maillard@gmail.com>, 2015
+# Xavier Ordoquy <xordoquy@linovia.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
-"PO-Revision-Date: 2015-01-30 16:27+0000\n"
-"Last-Translator: Thomas Christie <tom@tomchristie.com>\n"
+"PO-Revision-Date: 2015-03-19 22:23+0000\n"
+"Last-Translator: Xavier Ordoquy <xordoquy@linovia.com>\n"
"Language-Team: French (http://www.transifex.com/projects/p/django-rest-framework/language/fr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -73,7 +75,7 @@ msgstr "Vous n'avez pas la permission d'effectuer cette action."
#: exceptions.py:93
msgid "Not found."
-msgstr ""
+msgstr "Pas trouvé."
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
@@ -81,15 +83,15 @@ msgstr "Méthode \"{method}\" non autorisée."
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
-msgstr ""
+msgstr "L'en-tête « Accept » n'a pas pu être satisfaite."
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
-msgstr ""
+msgstr "Type de média \"{media_type}\" non supporté."
#: exceptions.py:134
msgid "Request was throttled."
-msgstr ""
+msgstr "Requête ralentie."
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
@@ -136,11 +138,11 @@ msgstr "Saisissez une URL valide."
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
-msgstr ""
+msgstr "\"{value}\" n'est pas un UUID valide."
#: fields.py:657
msgid "A valid integer is required."
-msgstr "Saisissez un nombre entier valide."
+msgstr "Un nombre entier valide est requis."
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
@@ -175,23 +177,23 @@ msgstr "Assurez-vous qu'il n'y a pas plus de {max_whole_digits} chiffres avant l
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
-msgstr ""
+msgstr "La date + heure n'a pas le bon format. Utilisez un des formats suivants : {format}."
#: fields.py:814
msgid "Expected a datetime but got a date."
-msgstr ""
+msgstr "Attendait une date + heure mais a reçu une date."
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
-msgstr ""
+msgstr "La date n'a pas le bon format. Utilisez un des formats suivants : {format}."
#: fields.py:879
msgid "Expected a date but got a datetime."
-msgstr ""
+msgstr "Attendait une date mais a reçu une date + heure."
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
-msgstr ""
+msgstr "L'heure n'a pas le bon format. Utilisez un des formats suivants : {format}."
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
@@ -199,7 +201,7 @@ msgstr "\"{input}\" n'est pas un choix valide."
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
-msgstr ""
+msgstr "Attendait une liste d'éléments mais a reçu \"{input_type}\"."
#: fields.py:1067
msgid "No file was submitted."
@@ -231,7 +233,7 @@ msgstr "Transférez une image valide. Le fichier que vous avez transféré n'est
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
-msgstr ""
+msgstr "Attendait un dictionnaire d'éléments mais a reçu \"{input_type}\"."
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
@@ -239,7 +241,7 @@ msgstr "Page \"{page_number}\" non valide : {message}."
#: pagination.py:442
msgid "Invalid cursor"
-msgstr ""
+msgstr "Curseur non valide"
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
@@ -247,23 +249,23 @@ msgstr "Clé primaire \"{pk_value}\" non valide - l'objet n'existe pas."
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
-msgstr ""
+msgstr "Type incorrect. Attendait une clé primaire, a reçu {data_type}."
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
-msgstr ""
+msgstr "Lien non valide : pas d'URL correspondante."
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
-msgstr ""
+msgstr "Lien non valide : URL correspondante incorrecte."
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
-msgstr ""
+msgstr "Lien non valide : l'objet n'existe pas."
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
-msgstr ""
+msgstr "Type incorrect. Attendait une URL, a reçu {data_type}."
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
@@ -275,7 +277,7 @@ msgstr "Valeur non valide."
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
-msgstr ""
+msgstr "Donnée non valide. Attendait un dictionnaire, a reçu {datatype}."
#: validators.py:22
msgid "This field must be unique."
diff --git a/rest_framework/locale/pt_PT/LC_MESSAGES/django.mo b/rest_framework/locale/pt_PT/LC_MESSAGES/django.mo
new file mode 100644
index 00000000..35db0dd7
--- /dev/null
+++ b/rest_framework/locale/pt_PT/LC_MESSAGES/django.mo
Binary files differ
diff --git a/rest_framework/locale/pt_PT/LC_MESSAGES/django.po b/rest_framework/locale/pt_PT/LC_MESSAGES/django.po
new file mode 100644
index 00000000..9da496fe
--- /dev/null
+++ b/rest_framework/locale/pt_PT/LC_MESSAGES/django.po
@@ -0,0 +1,324 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Django REST framework\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-01-30 16:23+0000\n"
+"PO-Revision-Date: 2015-01-02 10:46+0000\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/django-rest-framework/language/pt_PT/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: pt_PT\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: authentication.py:69
+msgid "Invalid basic header. No credentials provided."
+msgstr ""
+
+#: authentication.py:72
+msgid "Invalid basic header. Credentials string should not contain spaces."
+msgstr ""
+
+#: authentication.py:78
+msgid "Invalid basic header. Credentials not correctly base64 encoded."
+msgstr ""
+
+#: authentication.py:90
+msgid "Invalid username/password."
+msgstr ""
+
+#: authentication.py:156
+msgid "Invalid token header. No credentials provided."
+msgstr ""
+
+#: authentication.py:159
+msgid "Invalid token header. Token string should not contain spaces."
+msgstr ""
+
+#: authentication.py:168
+msgid "Invalid token."
+msgstr ""
+
+#: authentication.py:171
+msgid "User inactive or deleted."
+msgstr ""
+
+#: exceptions.py:38
+msgid "A server error occurred."
+msgstr ""
+
+#: exceptions.py:73
+msgid "Malformed request."
+msgstr ""
+
+#: exceptions.py:78
+msgid "Incorrect authentication credentials."
+msgstr ""
+
+#: exceptions.py:83
+msgid "Authentication credentials were not provided."
+msgstr ""
+
+#: exceptions.py:88
+msgid "You do not have permission to perform this action."
+msgstr ""
+
+#: exceptions.py:93
+msgid "Not found."
+msgstr ""
+
+#: exceptions.py:98
+msgid "Method \"{method}\" not allowed."
+msgstr ""
+
+#: exceptions.py:109
+msgid "Could not satisfy the request Accept header."
+msgstr ""
+
+#: exceptions.py:121
+msgid "Unsupported media type \"{media_type}\" in request."
+msgstr ""
+
+#: exceptions.py:134
+msgid "Request was throttled."
+msgstr ""
+
+#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
+#: validators.py:155
+msgid "This field is required."
+msgstr ""
+
+#: fields.py:154
+msgid "This field may not be null."
+msgstr ""
+
+#: fields.py:487 fields.py:515
+msgid "\"{input}\" is not a valid boolean."
+msgstr ""
+
+#: fields.py:550
+msgid "This field may not be blank."
+msgstr ""
+
+#: fields.py:551 fields.py:1324
+msgid "Ensure this field has no more than {max_length} characters."
+msgstr ""
+
+#: fields.py:552
+msgid "Ensure this field has at least {min_length} characters."
+msgstr ""
+
+#: fields.py:587
+msgid "Enter a valid email address."
+msgstr ""
+
+#: fields.py:604
+msgid "This value does not match the required pattern."
+msgstr ""
+
+#: fields.py:615
+msgid ""
+"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
+"hyphens."
+msgstr ""
+
+#: fields.py:627
+msgid "Enter a valid URL."
+msgstr ""
+
+#: fields.py:638
+msgid "\"{value}\" is not a valid UUID."
+msgstr ""
+
+#: fields.py:657
+msgid "A valid integer is required."
+msgstr ""
+
+#: fields.py:658 fields.py:692 fields.py:725
+msgid "Ensure this value is less than or equal to {max_value}."
+msgstr ""
+
+#: fields.py:659 fields.py:693 fields.py:726
+msgid "Ensure this value is greater than or equal to {min_value}."
+msgstr ""
+
+#: fields.py:660 fields.py:694 fields.py:730
+msgid "String value too large."
+msgstr ""
+
+#: fields.py:691 fields.py:724
+msgid "A valid number is required."
+msgstr ""
+
+#: fields.py:727
+msgid "Ensure that there are no more than {max_digits} digits in total."
+msgstr ""
+
+#: fields.py:728
+msgid ""
+"Ensure that there are no more than {max_decimal_places} decimal places."
+msgstr ""
+
+#: fields.py:729
+msgid ""
+"Ensure that there are no more than {max_whole_digits} digits before the "
+"decimal point."
+msgstr ""
+
+#: fields.py:813
+msgid "Datetime has wrong format. Use one of these formats instead: {format}."
+msgstr ""
+
+#: fields.py:814
+msgid "Expected a datetime but got a date."
+msgstr ""
+
+#: fields.py:878
+msgid "Date has wrong format. Use one of these formats instead: {format}."
+msgstr ""
+
+#: fields.py:879
+msgid "Expected a date but got a datetime."
+msgstr ""
+
+#: fields.py:936
+msgid "Time has wrong format. Use one of these formats instead: {format}."
+msgstr ""
+
+#: fields.py:992 fields.py:1036
+msgid "\"{input}\" is not a valid choice."
+msgstr ""
+
+#: fields.py:1037 fields.py:1151 serializers.py:482
+msgid "Expected a list of items but got type \"{input_type}\"."
+msgstr ""
+
+#: fields.py:1067
+msgid "No file was submitted."
+msgstr ""
+
+#: fields.py:1068
+msgid ""
+"The submitted data was not a file. Check the encoding type on the form."
+msgstr ""
+
+#: fields.py:1069
+msgid "No filename could be determined."
+msgstr ""
+
+#: fields.py:1070
+msgid "The submitted file is empty."
+msgstr ""
+
+#: fields.py:1071
+msgid ""
+"Ensure this filename has at most {max_length} characters (it has {length})."
+msgstr ""
+
+#: fields.py:1113
+msgid ""
+"Upload a valid image. The file you uploaded was either not an image or a "
+"corrupted image."
+msgstr ""
+
+#: fields.py:1188
+msgid "Expected a dictionary of items but got type \"{input_type}\"."
+msgstr ""
+
+#: pagination.py:221
+msgid "Invalid page \"{page_number}\": {message}."
+msgstr ""
+
+#: pagination.py:442
+msgid "Invalid cursor"
+msgstr ""
+
+#: relations.py:133
+msgid "Invalid pk \"{pk_value}\" - object does not exist."
+msgstr ""
+
+#: relations.py:134
+msgid "Incorrect type. Expected pk value, received {data_type}."
+msgstr ""
+
+#: relations.py:157
+msgid "Invalid hyperlink - No URL match."
+msgstr ""
+
+#: relations.py:158
+msgid "Invalid hyperlink - Incorrect URL match."
+msgstr ""
+
+#: relations.py:159
+msgid "Invalid hyperlink - Object does not exist."
+msgstr ""
+
+#: relations.py:160
+msgid "Incorrect type. Expected URL string, received {data_type}."
+msgstr ""
+
+#: relations.py:295
+msgid "Object with {slug_name}={value} does not exist."
+msgstr ""
+
+#: relations.py:296
+msgid "Invalid value."
+msgstr ""
+
+#: serializers.py:299
+msgid "Invalid data. Expected a dictionary, but got {datatype}."
+msgstr ""
+
+#: validators.py:22
+msgid "This field must be unique."
+msgstr ""
+
+#: validators.py:76
+msgid "The fields {field_names} must make a unique set."
+msgstr ""
+
+#: validators.py:219
+msgid "This field must be unique for the \"{date_field}\" date."
+msgstr ""
+
+#: validators.py:234
+msgid "This field must be unique for the \"{date_field}\" month."
+msgstr ""
+
+#: validators.py:247
+msgid "This field must be unique for the \"{date_field}\" year."
+msgstr ""
+
+#: versioning.py:39
+msgid "Invalid version in \"Accept\" header."
+msgstr ""
+
+#: versioning.py:70 versioning.py:112
+msgid "Invalid version in URL path."
+msgstr ""
+
+#: versioning.py:138
+msgid "Invalid version in hostname."
+msgstr ""
+
+#: versioning.py:160
+msgid "Invalid version in query parameter."
+msgstr ""
+
+#: authtoken/serializers.py:20
+msgid "User account is disabled."
+msgstr ""
+
+#: authtoken/serializers.py:23
+msgid "Unable to log in with provided credentials."
+msgstr ""
+
+#: authtoken/serializers.py:26
+msgid "Must include \"username\" and \"password\"."
+msgstr ""
diff --git a/rest_framework/locale/vi/LC_MESSAGES/django.mo b/rest_framework/locale/vi/LC_MESSAGES/django.mo
new file mode 100644
index 00000000..dea3a362
--- /dev/null
+++ b/rest_framework/locale/vi/LC_MESSAGES/django.mo
Binary files differ
diff --git a/rest_framework/locale/vi/LC_MESSAGES/django.po b/rest_framework/locale/vi/LC_MESSAGES/django.po
new file mode 100644
index 00000000..b378e4de
--- /dev/null
+++ b/rest_framework/locale/vi/LC_MESSAGES/django.po
@@ -0,0 +1,324 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: Django REST framework\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-01-30 16:23+0000\n"
+"PO-Revision-Date: 2015-01-02 10:46+0000\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: Vietnamese (http://www.transifex.com/projects/p/django-rest-framework/language/vi/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: vi\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: authentication.py:69
+msgid "Invalid basic header. No credentials provided."
+msgstr ""
+
+#: authentication.py:72
+msgid "Invalid basic header. Credentials string should not contain spaces."
+msgstr ""
+
+#: authentication.py:78
+msgid "Invalid basic header. Credentials not correctly base64 encoded."
+msgstr ""
+
+#: authentication.py:90
+msgid "Invalid username/password."
+msgstr ""
+
+#: authentication.py:156
+msgid "Invalid token header. No credentials provided."
+msgstr ""
+
+#: authentication.py:159
+msgid "Invalid token header. Token string should not contain spaces."
+msgstr ""
+
+#: authentication.py:168
+msgid "Invalid token."
+msgstr ""
+
+#: authentication.py:171
+msgid "User inactive or deleted."
+msgstr ""
+
+#: exceptions.py:38
+msgid "A server error occurred."
+msgstr ""
+
+#: exceptions.py:73
+msgid "Malformed request."
+msgstr ""
+
+#: exceptions.py:78
+msgid "Incorrect authentication credentials."
+msgstr ""
+
+#: exceptions.py:83
+msgid "Authentication credentials were not provided."
+msgstr ""
+
+#: exceptions.py:88
+msgid "You do not have permission to perform this action."
+msgstr ""
+
+#: exceptions.py:93
+msgid "Not found."
+msgstr ""
+
+#: exceptions.py:98
+msgid "Method \"{method}\" not allowed."
+msgstr ""
+
+#: exceptions.py:109
+msgid "Could not satisfy the request Accept header."
+msgstr ""
+
+#: exceptions.py:121
+msgid "Unsupported media type \"{media_type}\" in request."
+msgstr ""
+
+#: exceptions.py:134
+msgid "Request was throttled."
+msgstr ""
+
+#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
+#: validators.py:155
+msgid "This field is required."
+msgstr ""
+
+#: fields.py:154
+msgid "This field may not be null."
+msgstr ""
+
+#: fields.py:487 fields.py:515
+msgid "\"{input}\" is not a valid boolean."
+msgstr ""
+
+#: fields.py:550
+msgid "This field may not be blank."
+msgstr ""
+
+#: fields.py:551 fields.py:1324
+msgid "Ensure this field has no more than {max_length} characters."
+msgstr ""
+
+#: fields.py:552
+msgid "Ensure this field has at least {min_length} characters."
+msgstr ""
+
+#: fields.py:587
+msgid "Enter a valid email address."
+msgstr ""
+
+#: fields.py:604
+msgid "This value does not match the required pattern."
+msgstr ""
+
+#: fields.py:615
+msgid ""
+"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
+"hyphens."
+msgstr ""
+
+#: fields.py:627
+msgid "Enter a valid URL."
+msgstr ""
+
+#: fields.py:638
+msgid "\"{value}\" is not a valid UUID."
+msgstr ""
+
+#: fields.py:657
+msgid "A valid integer is required."
+msgstr ""
+
+#: fields.py:658 fields.py:692 fields.py:725
+msgid "Ensure this value is less than or equal to {max_value}."
+msgstr ""
+
+#: fields.py:659 fields.py:693 fields.py:726
+msgid "Ensure this value is greater than or equal to {min_value}."
+msgstr ""
+
+#: fields.py:660 fields.py:694 fields.py:730
+msgid "String value too large."
+msgstr ""
+
+#: fields.py:691 fields.py:724
+msgid "A valid number is required."
+msgstr ""
+
+#: fields.py:727
+msgid "Ensure that there are no more than {max_digits} digits in total."
+msgstr ""
+
+#: fields.py:728
+msgid ""
+"Ensure that there are no more than {max_decimal_places} decimal places."
+msgstr ""
+
+#: fields.py:729
+msgid ""
+"Ensure that there are no more than {max_whole_digits} digits before the "
+"decimal point."
+msgstr ""
+
+#: fields.py:813
+msgid "Datetime has wrong format. Use one of these formats instead: {format}."
+msgstr ""
+
+#: fields.py:814
+msgid "Expected a datetime but got a date."
+msgstr ""
+
+#: fields.py:878
+msgid "Date has wrong format. Use one of these formats instead: {format}."
+msgstr ""
+
+#: fields.py:879
+msgid "Expected a date but got a datetime."
+msgstr ""
+
+#: fields.py:936
+msgid "Time has wrong format. Use one of these formats instead: {format}."
+msgstr ""
+
+#: fields.py:992 fields.py:1036
+msgid "\"{input}\" is not a valid choice."
+msgstr ""
+
+#: fields.py:1037 fields.py:1151 serializers.py:482
+msgid "Expected a list of items but got type \"{input_type}\"."
+msgstr ""
+
+#: fields.py:1067
+msgid "No file was submitted."
+msgstr ""
+
+#: fields.py:1068
+msgid ""
+"The submitted data was not a file. Check the encoding type on the form."
+msgstr ""
+
+#: fields.py:1069
+msgid "No filename could be determined."
+msgstr ""
+
+#: fields.py:1070
+msgid "The submitted file is empty."
+msgstr ""
+
+#: fields.py:1071
+msgid ""
+"Ensure this filename has at most {max_length} characters (it has {length})."
+msgstr ""
+
+#: fields.py:1113
+msgid ""
+"Upload a valid image. The file you uploaded was either not an image or a "
+"corrupted image."
+msgstr ""
+
+#: fields.py:1188
+msgid "Expected a dictionary of items but got type \"{input_type}\"."
+msgstr ""
+
+#: pagination.py:221
+msgid "Invalid page \"{page_number}\": {message}."
+msgstr ""
+
+#: pagination.py:442
+msgid "Invalid cursor"
+msgstr ""
+
+#: relations.py:133
+msgid "Invalid pk \"{pk_value}\" - object does not exist."
+msgstr ""
+
+#: relations.py:134
+msgid "Incorrect type. Expected pk value, received {data_type}."
+msgstr ""
+
+#: relations.py:157
+msgid "Invalid hyperlink - No URL match."
+msgstr ""
+
+#: relations.py:158
+msgid "Invalid hyperlink - Incorrect URL match."
+msgstr ""
+
+#: relations.py:159
+msgid "Invalid hyperlink - Object does not exist."
+msgstr ""
+
+#: relations.py:160
+msgid "Incorrect type. Expected URL string, received {data_type}."
+msgstr ""
+
+#: relations.py:295
+msgid "Object with {slug_name}={value} does not exist."
+msgstr ""
+
+#: relations.py:296
+msgid "Invalid value."
+msgstr ""
+
+#: serializers.py:299
+msgid "Invalid data. Expected a dictionary, but got {datatype}."
+msgstr ""
+
+#: validators.py:22
+msgid "This field must be unique."
+msgstr ""
+
+#: validators.py:76
+msgid "The fields {field_names} must make a unique set."
+msgstr ""
+
+#: validators.py:219
+msgid "This field must be unique for the \"{date_field}\" date."
+msgstr ""
+
+#: validators.py:234
+msgid "This field must be unique for the \"{date_field}\" month."
+msgstr ""
+
+#: validators.py:247
+msgid "This field must be unique for the \"{date_field}\" year."
+msgstr ""
+
+#: versioning.py:39
+msgid "Invalid version in \"Accept\" header."
+msgstr ""
+
+#: versioning.py:70 versioning.py:112
+msgid "Invalid version in URL path."
+msgstr ""
+
+#: versioning.py:138
+msgid "Invalid version in hostname."
+msgstr ""
+
+#: versioning.py:160
+msgid "Invalid version in query parameter."
+msgstr ""
+
+#: authtoken/serializers.py:20
+msgid "User account is disabled."
+msgstr ""
+
+#: authtoken/serializers.py:23
+msgid "Unable to log in with provided credentials."
+msgstr ""
+
+#: authtoken/serializers.py:26
+msgid "Must include \"username\" and \"password\"."
+msgstr ""
diff --git a/rest_framework/metadata.py b/rest_framework/metadata.py
index bf3611aa..b2c48b81 100644
--- a/rest_framework/metadata.py
+++ b/rest_framework/metadata.py
@@ -36,6 +36,7 @@ class SimpleMetadata(BaseMetadata):
label_lookup = ClassLookupDict({
serializers.Field: 'field',
serializers.BooleanField: 'boolean',
+ serializers.NullBooleanField: 'boolean',
serializers.CharField: 'string',
serializers.URLField: 'url',
serializers.EmailField: 'email',
diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py
index 920d2bc4..2350344c 100644
--- a/rest_framework/renderers.py
+++ b/rest_framework/renderers.py
@@ -421,6 +421,14 @@ class BrowsableAPIRenderer(BaseRenderer):
return False # Doesn't have permissions
return True
+ def _get_serializer(self, serializer_class, view_instance, request, *args, **kwargs):
+ kwargs['context'] = {
+ 'request': request,
+ 'format': self.format,
+ 'view': view_instance
+ }
+ return serializer_class(*args, **kwargs)
+
def get_rendered_html_form(self, data, view, method, request):
"""
Return a string representing a rendered HTML form, possibly bound to
@@ -457,8 +465,11 @@ class BrowsableAPIRenderer(BaseRenderer):
if method in ('DELETE', 'OPTIONS'):
return True # Don't actually need to return a form
+ has_serializer = getattr(view, 'get_serializer', None)
+ has_serializer_class = getattr(view, 'serializer_class', None)
+
if (
- not getattr(view, 'get_serializer', None) or
+ (not has_serializer and not has_serializer_class) or
not any(is_form_media_type(parser.media_type) for parser in view.parser_classes)
):
return
@@ -466,10 +477,19 @@ class BrowsableAPIRenderer(BaseRenderer):
if existing_serializer is not None:
serializer = existing_serializer
else:
- if method in ('PUT', 'PATCH'):
- serializer = view.get_serializer(instance=instance, **kwargs)
+ if has_serializer:
+ if method in ('PUT', 'PATCH'):
+ serializer = view.get_serializer(instance=instance, **kwargs)
+ else:
+ serializer = view.get_serializer(**kwargs)
else:
- serializer = view.get_serializer(**kwargs)
+ # at this point we must have a serializer_class
+ if method in ('PUT', 'PATCH'):
+ serializer = self._get_serializer(view.serializer_class, view,
+ request, instance=instance, **kwargs)
+ else:
+ serializer = self._get_serializer(view.serializer_class, view,
+ request, **kwargs)
if hasattr(serializer, 'initial_data'):
serializer.is_valid()
diff --git a/rest_framework/response.py b/rest_framework/response.py
index c21c60a2..9319e708 100644
--- a/rest_framework/response.py
+++ b/rest_framework/response.py
@@ -5,7 +5,7 @@ it is initialized with unrendered data, instead of a pre-rendered string.
The appropriate renderer is called during Django's template response rendering.
"""
from __future__ import unicode_literals
-from django.core.handlers.wsgi import STATUS_CODE_TEXT
+from django.utils.six.moves.http_client import responses
from django.template.response import SimpleTemplateResponse
from django.utils import six
@@ -77,7 +77,7 @@ class Response(SimpleTemplateResponse):
"""
# TODO: Deprecate and use a template tag instead
# TODO: Status code text for RFC 6585 status codes
- return STATUS_CODE_TEXT.get(self.status_code, '')
+ return responses.get(self.status_code, '')
def __getstate__(self):
"""
diff --git a/rest_framework/reverse.py b/rest_framework/reverse.py
index a251d99d..e6d3f563 100644
--- a/rest_framework/reverse.py
+++ b/rest_framework/reverse.py
@@ -3,6 +3,7 @@ Provide urlresolver functions that return fully qualified URLs or view names
"""
from __future__ import unicode_literals
from django.core.urlresolvers import reverse as django_reverse
+from django.core.urlresolvers import NoReverseMatch
from django.utils import six
from django.utils.functional import lazy
@@ -15,7 +16,13 @@ def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra
"""
scheme = getattr(request, 'versioning_scheme', None)
if scheme is not None:
- return scheme.reverse(viewname, args, kwargs, request, format, **extra)
+ try:
+ return scheme.reverse(viewname, args, kwargs, request, format, **extra)
+ except NoReverseMatch:
+ # In case the versioning scheme reversal fails, fallback to the
+ # default implementation
+ pass
+
return _reverse(viewname, args, kwargs, request, format, **extra)
diff --git a/rest_framework/static/rest_framework/js/default.js b/rest_framework/static/rest_framework/js/default.js
index c8812132..22e5efde 100644
--- a/rest_framework/static/rest_framework/js/default.js
+++ b/rest_framework/static/rest_framework/js/default.js
@@ -45,6 +45,10 @@ var selectedTab = null;
var selectedTabName = getCookie('tabstyle');
if (selectedTabName) {
+ selectedTabName = selectedTabName.replace(/[^a-z-]/g, '');
+}
+
+if (selectedTabName) {
selectedTab = $('.form-switcher a[name=' + selectedTabName + ']');
}
diff --git a/tests/test_metadata.py b/tests/test_metadata.py
index 3a435f02..731aedba 100644
--- a/tests/test_metadata.py
+++ b/tests/test_metadata.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from rest_framework import exceptions, serializers, status, views, versioning
+from rest_framework import exceptions, metadata, serializers, status, views, versioning
from rest_framework.request import Request
from rest_framework.renderers import BrowsableAPIRenderer
from rest_framework.test import APIRequestFactory
@@ -207,3 +207,8 @@ class TestMetadata:
scheme = versioning.QueryParameterVersioning
view = ExampleView.as_view(versioning_class=scheme)
view(request=request)
+
+ def test_null_boolean_field_info_type(self):
+ options = metadata.SimpleMetadata()
+ field_info = options.get_field_info(serializers.NullBooleanField())
+ assert field_info['type'] == 'boolean'
diff --git a/tests/test_reverse.py b/tests/test_reverse.py
index 675a9d5a..08c27023 100644
--- a/tests/test_reverse.py
+++ b/tests/test_reverse.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
from django.conf.urls import patterns, url
+from django.core.urlresolvers import NoReverseMatch
from django.test import TestCase
from rest_framework.reverse import reverse
from rest_framework.test import APIRequestFactory
@@ -16,6 +17,18 @@ urlpatterns = patterns(
)
+class MockVersioningScheme(object):
+
+ def __init__(self, raise_error=False):
+ self.raise_error = raise_error
+
+ def reverse(self, *args, **kwargs):
+ if self.raise_error:
+ raise NoReverseMatch()
+
+ return 'http://scheme-reversed/view'
+
+
class ReverseTests(TestCase):
"""
Tests for fully qualified URLs when using `reverse`.
@@ -26,3 +39,17 @@ class ReverseTests(TestCase):
request = factory.get('/view')
url = reverse('view', request=request)
self.assertEqual(url, 'http://testserver/view')
+
+ def test_reverse_with_versioning_scheme(self):
+ request = factory.get('/view')
+ request.versioning_scheme = MockVersioningScheme()
+
+ url = reverse('view', request=request)
+ self.assertEqual(url, 'http://scheme-reversed/view')
+
+ def test_reverse_with_versioning_scheme_fallback_to_default_on_error(self):
+ request = factory.get('/view')
+ request.versioning_scheme = MockVersioningScheme(raise_error=True)
+
+ url = reverse('view', request=request)
+ self.assertEqual(url, 'http://testserver/view')
diff --git a/tests/test_versioning.py b/tests/test_versioning.py
index 90ad8afd..88ae56dd 100644
--- a/tests/test_versioning.py
+++ b/tests/test_versioning.py
@@ -7,6 +7,7 @@ from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.test import APIRequestFactory, APITestCase
from rest_framework.versioning import NamespaceVersioning
+from rest_framework.relations import PKOnlyObject
import pytest
@@ -234,7 +235,7 @@ class TestInvalidVersion:
class TestHyperlinkedRelatedField(UsingURLPatterns, APITestCase):
included = [
- url(r'^namespaced/(?P<pk>\d+)/$', dummy_view, name='namespaced'),
+ url(r'^namespaced/(?P<pk>\d+)/$', dummy_pk_view, name='namespaced'),
]
urlpatterns = [
@@ -262,3 +263,44 @@ class TestHyperlinkedRelatedField(UsingURLPatterns, APITestCase):
assert self.field.to_internal_value('/v1/namespaced/3/') == 'object 3'
with pytest.raises(serializers.ValidationError):
self.field.to_internal_value('/v2/namespaced/3/')
+
+
+class TestNamespaceVersioningHyperlinkedRelatedFieldScheme(UsingURLPatterns, APITestCase):
+ included = [
+ url(r'^namespaced/(?P<pk>\d+)/$', dummy_pk_view, name='namespaced'),
+ ]
+
+ urlpatterns = [
+ url(r'^v1/', include(included, namespace='v1')),
+ url(r'^v2/', include(included, namespace='v2')),
+ url(r'^non-api/(?P<pk>\d+)/$', dummy_pk_view, name='non-api-view')
+ ]
+
+ def _create_field(self, view_name, version):
+ request = factory.get("/")
+ request.versioning_scheme = NamespaceVersioning()
+ request.version = version
+
+ field = serializers.HyperlinkedRelatedField(
+ view_name=view_name,
+ read_only=True)
+ field._context = {'request': request}
+ return field
+
+ def test_api_url_is_properly_reversed_with_v1(self):
+ field = self._create_field('namespaced', 'v1')
+ assert field.to_representation(PKOnlyObject(3)) == 'http://testserver/v1/namespaced/3/'
+
+ def test_api_url_is_properly_reversed_with_v2(self):
+ field = self._create_field('namespaced', 'v2')
+ assert field.to_representation(PKOnlyObject(5)) == 'http://testserver/v2/namespaced/5/'
+
+ def test_non_api_url_is_properly_reversed_regardless_of_the_version(self):
+ """
+ Regression test for #2711
+ """
+ field = self._create_field('non-api-view', 'v1')
+ assert field.to_representation(PKOnlyObject(10)) == 'http://testserver/non-api/10/'
+
+ field = self._create_field('non-api-view', 'v2')
+ assert field.to_representation(PKOnlyObject(10)) == 'http://testserver/non-api/10/'
diff --git a/tox.ini b/tox.ini
index c986250c..befef744 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,7 +3,7 @@ envlist =
py27-{flake8,docs},
{py26,py27}-django14,
{py26,py27,py32,py33,py34}-django{15,16},
- {py27,py32,py33,py34}-django{17,18beta}
+ {py27,py32,py33,py34}-django{17,18}
[testenv]
commands = ./runtests.py --fast
@@ -14,14 +14,14 @@ deps =
django15: Django==1.5.6 # Should track minimum supported
django16: Django==1.6.3 # Should track minimum supported
django17: Django==1.7.2 # Should track maximum supported
- django18beta: https://www.djangoproject.com/download/1.8b1/tarball/
+ django18: Django==1.8 # Should track maximum supported
-rrequirements/requirements-testing.txt
-rrequirements/requirements-optionals.txt
[testenv:py27-flake8]
deps =
- -rrequirements/requirements-testing.txt
-rrequirements/requirements-codestyle.txt
+ -rrequirements/requirements-testing.txt
commands = ./runtests.py --lintonly
[testenv:py27-docs]