From 6c108c459d8cfeda46b8e045ef750c01dd0ffcaa Mon Sep 17 00:00:00 2001
From: Ian Foote
Date: Wed, 16 Apr 2014 12:32:04 +0100
Subject: Allow customising ChoiceField blank display value
---
rest_framework/fields.py | 8 ++++++--
rest_framework/tests/test_fields.py | 9 +++++++++
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index 946a5954..d9521cd4 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -509,12 +509,16 @@ class ChoiceField(WritableField):
'the available choices.'),
}
- def __init__(self, choices=(), *args, **kwargs):
+ def __init__(self, choices=(), blank_display_value=None, *args, **kwargs):
self.empty = kwargs.pop('empty', '')
super(ChoiceField, self).__init__(*args, **kwargs)
self.choices = choices
if not self.required:
- self.choices = BLANK_CHOICE_DASH + self.choices
+ if blank_display_value is None:
+ blank_choice = BLANK_CHOICE_DASH
+ else:
+ blank_choice = [('', blank_display_value)]
+ self.choices = blank_choice + self.choices
def _get_choices(self):
return self._choices
diff --git a/rest_framework/tests/test_fields.py b/rest_framework/tests/test_fields.py
index e127feef..63dff718 100644
--- a/rest_framework/tests/test_fields.py
+++ b/rest_framework/tests/test_fields.py
@@ -706,6 +706,15 @@ class ChoiceFieldTests(TestCase):
f = serializers.ChoiceField(required=False, choices=SAMPLE_CHOICES)
self.assertEqual(f.choices, models.fields.BLANK_CHOICE_DASH + SAMPLE_CHOICES)
+ def test_blank_choice_display(self):
+ blank = 'No Preference'
+ f = serializers.ChoiceField(
+ required=False,
+ choices=SAMPLE_CHOICES,
+ blank_display_value=blank,
+ )
+ self.assertEqual(f.choices, [('', blank)] + SAMPLE_CHOICES)
+
def test_invalid_choice_model(self):
s = ChoiceFieldModelSerializer(data={'choice': 'wrong_value'})
self.assertFalse(s.is_valid())
--
cgit v1.2.3
From 4e33ff05d9aabee0a90bfb0ef8ce58a5d274b9a2 Mon Sep 17 00:00:00 2001
From: Lucian Mocanu
Date: Sun, 4 May 2014 00:12:08 +0200
Subject: Automatically set the field name as value for the HTML `id` attribute
on the rendered widget.
---
rest_framework/fields.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index 8cdc5551..e6733849 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -154,7 +154,12 @@ class Field(object):
def widget_html(self):
if not self.widget:
return ''
- return self.widget.render(self._name, self._value)
+
+ attrs = {}
+ if 'id' not in self.widget.attrs:
+ attrs['id'] = self._name
+
+ return self.widget.render(self._name, self._value, attrs=attrs)
def label_tag(self):
return '' % (self._name, self.label)
--
cgit v1.2.3
From 708c7b3a816c3c2df7847695044ef852dc89e72c Mon Sep 17 00:00:00 2001
From: Lucian Mocanu
Date: Tue, 6 May 2014 14:17:51 +0200
Subject: Added test case to check if the proper attributes are set on html
widgets.
---
rest_framework/tests/test_fields.py | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/rest_framework/tests/test_fields.py b/rest_framework/tests/test_fields.py
index e127feef..03f79cf4 100644
--- a/rest_framework/tests/test_fields.py
+++ b/rest_framework/tests/test_fields.py
@@ -4,6 +4,7 @@ General serializer field tests.
from __future__ import unicode_literals
import datetime
+import re
from decimal import Decimal
from uuid import uuid4
from django.core import validators
@@ -103,6 +104,16 @@ class BasicFieldTests(TestCase):
keys = list(field.to_native(ret).keys())
self.assertEqual(keys, ['c', 'b', 'a', 'z'])
+ def test_widget_html_attributes(self):
+ """
+ Make sure widget_html() renders the correct attributes
+ """
+ r = re.compile('(\S+)=["\']?((?:.(?!["\']?\s+(?:\S+)=|[>"\']))+.)["\']?')
+ form = TimeFieldModelSerializer().data
+ attributes = r.findall(form.fields['clock'].widget_html())
+ self.assertIn(('name', 'clock'), attributes)
+ self.assertIn(('id', 'clock'), attributes)
+
class DateFieldTest(TestCase):
"""
--
cgit v1.2.3
From 11115fde9cc8f70dfd85ce937893d67fd061f3c1 Mon Sep 17 00:00:00 2001
From: Elliott
Date: Wed, 7 May 2014 11:37:20 -0700
Subject: Add colon to time zone offset in readable_datetime_formats
---
rest_framework/fields.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index 68b95682..9f53a000 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -62,7 +62,7 @@ def get_component(obj, attr_name):
def readable_datetime_formats(formats):
format = ', '.join(formats).replace(ISO_8601,
- 'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HHMM|-HHMM|Z]')
+ 'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]')
return humanize_strptime(format)
--
cgit v1.2.3
From 0ff474d7c4882a31f8fb133caa82d0368b0406c2 Mon Sep 17 00:00:00 2001
From: Carlton Gibson
Date: Thu, 8 May 2014 11:20:03 +0200
Subject: Updated failing test from #1575
---
rest_framework/tests/test_fields.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/rest_framework/tests/test_fields.py b/rest_framework/tests/test_fields.py
index e127feef..894b5b3c 100644
--- a/rest_framework/tests/test_fields.py
+++ b/rest_framework/tests/test_fields.py
@@ -312,7 +312,7 @@ class DateTimeFieldTest(TestCase):
f.from_native('04:61:59')
except validators.ValidationError as e:
self.assertEqual(e.messages, ["Datetime has wrong format. Use one of these formats instead: "
- "YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HHMM|-HHMM|Z]"])
+ "YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]"])
else:
self.fail("ValidationError was not properly raised")
@@ -326,7 +326,7 @@ class DateTimeFieldTest(TestCase):
f.from_native('04 -- 31')
except validators.ValidationError as e:
self.assertEqual(e.messages, ["Datetime has wrong format. Use one of these formats instead: "
- "YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HHMM|-HHMM|Z]"])
+ "YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]"])
else:
self.fail("ValidationError was not properly raised")
--
cgit v1.2.3
From 8ecb778cd23d5d561f2e9f4a3561bb1647257a89 Mon Sep 17 00:00:00 2001
From: Corey Farwell
Date: Sun, 11 May 2014 20:29:01 -0700
Subject: Enable testing on Python 3.4
---
.travis.yml | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index bd6d2539..0c9b4455 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,6 +5,7 @@ python:
- "2.7"
- "3.2"
- "3.3"
+ - "3.4"
env:
- DJANGO="https://www.djangoproject.com/download/1.7b2/tarball/"
@@ -41,4 +42,7 @@ matrix:
env: DJANGO="django==1.4.11"
- python: "3.3"
env: DJANGO="django==1.3.7"
-
+ - python: "3.4"
+ env: DJANGO="django==1.4.11"
+ - python: "3.4"
+ env: DJANGO="django==1.3.7"
--
cgit v1.2.3
From 768f537dcbb5d4f7429a74556559047bfd6f3078 Mon Sep 17 00:00:00 2001
From: Giorgos Logiotatidis
Date: Thu, 15 May 2014 15:34:31 +0300
Subject: Typo fix.
---
docs/api-guide/serializers.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md
index 7ee060af..0044f070 100644
--- a/docs/api-guide/serializers.md
+++ b/docs/api-guide/serializers.md
@@ -464,7 +464,7 @@ For more specific requirements such as specifying a different lookup for each fi
model = Account
fields = ('url', 'account_name', 'users', 'created')
-## Overiding the URL field behavior
+## Overriding the URL field behavior
The name of the URL field defaults to 'url'. You can override this globally, by using the `URL_FIELD_NAME` setting.
@@ -478,7 +478,7 @@ You can also override this on a per-serializer basis by using the `url_field_nam
**Note**: The generic view implementations normally generate a `Location` header in response to successful `POST` requests. Serializers using `url_field_name` option will not have this header automatically included by the view. If you need to do so you will ned to also override the view's `get_success_headers()` method.
-You can also overide the URL field's view name and lookup field without overriding the field explicitly, by using the `view_name` and `lookup_field` options, like so:
+You can also override the URL field's view name and lookup field without overriding the field explicitly, by using the `view_name` and `lookup_field` options, like so:
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
--
cgit v1.2.3
From e5556079fc2559916d62b766dc9776b03dc4256b Mon Sep 17 00:00:00 2001
From: Xavier Ordoquy
Date: Fri, 16 May 2014 00:50:16 +0200
Subject: Updated tox with Python 2.4
---
tox.ini | 28 +++++++++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/tox.ini b/tox.ini
index e2121005..35a108e5 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,10 +1,22 @@
[tox]
downloadcache = {toxworkdir}/cache/
-envlist = py3.3-django1.7,py3.2-django1.7,py2.7-django1.7,py3.3-django1.6,py3.2-django1.6,py2.7-django1.6,py2.6-django1.6,py3.3-django1.5,py3.2-django1.5,py2.7-django1.5,py2.6-django1.5,py2.7-django1.4,py2.6-django1.4,py2.7-django1.3,py2.6-django1.3
+envlist =
+ py3.4-django1.7,py3.3-django1.7,py3.2-django1.7,py2.7-django1.7,
+ py3.4-django1.6,py3.3-django1.6,py3.2-django1.6,py2.7-django1.6,py2.6-django1.6,
+ py3.4-django1.5,py3.3-django1.5,py3.2-django1.5,py2.7-django1.5,py2.6-django1.5,
+ py2.7-django1.4,py2.6-django1.4,
+ py2.7-django1.3,py2.6-django1.3
[testenv]
commands = {envpython} rest_framework/runtests/runtests.py
+[testenv:py3.4-django1.7]
+basepython = python3.4
+deps = https://www.djangoproject.com/download/1.7b2/tarball/
+ django-filter==0.7
+ defusedxml==0.3
+ Pillow==2.3.0
+
[testenv:py3.3-django1.7]
basepython = python3.3
deps = https://www.djangoproject.com/download/1.7b2/tarball/
@@ -30,6 +42,13 @@ deps = https://www.djangoproject.com/download/1.7b2/tarball/
django-guardian==1.1.1
Pillow==2.3.0
+[testenv:py3.4-django1.6]
+basepython = python3.3
+deps = Django==1.6.3
+ django-filter==0.7
+ defusedxml==0.3
+ Pillow==2.3.0
+
[testenv:py3.3-django1.6]
basepython = python3.3
deps = Django==1.6.3
@@ -66,6 +85,13 @@ deps = Django==1.6.3
django-guardian==1.1.1
Pillow==2.3.0
+[testenv:py3.4-django1.5]
+basepython = python3.3
+deps = django==1.5.6
+ django-filter==0.7
+ defusedxml==0.3
+ Pillow==2.3.0
+
[testenv:py3.3-django1.5]
basepython = python3.3
deps = django==1.5.6
--
cgit v1.2.3
From b370fb40b6bc0fd3f597fb8c2db59f0ca57a7ccd Mon Sep 17 00:00:00 2001
From: Xavier Ordoquy
Date: Fri, 16 May 2014 01:06:34 +0200
Subject: Typo in the Python version.
---
tox.ini | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tox.ini b/tox.ini
index 35a108e5..279f79cc 100644
--- a/tox.ini
+++ b/tox.ini
@@ -43,7 +43,7 @@ deps = https://www.djangoproject.com/download/1.7b2/tarball/
Pillow==2.3.0
[testenv:py3.4-django1.6]
-basepython = python3.3
+basepython = python3.4
deps = Django==1.6.3
django-filter==0.7
defusedxml==0.3
@@ -86,7 +86,7 @@ deps = Django==1.6.3
Pillow==2.3.0
[testenv:py3.4-django1.5]
-basepython = python3.3
+basepython = python3.4
deps = django==1.5.6
django-filter==0.7
defusedxml==0.3
--
cgit v1.2.3
From a704d5a206238c65765c1f02eb053e461675dda2 Mon Sep 17 00:00:00 2001
From: Xavier Ordoquy
Date: Fri, 16 May 2014 01:20:40 +0200
Subject: Fixed tests for python 3.4
---
rest_framework/tests/test_views.py | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/rest_framework/tests/test_views.py b/rest_framework/tests/test_views.py
index 65c7e50e..77b113ee 100644
--- a/rest_framework/tests/test_views.py
+++ b/rest_framework/tests/test_views.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+import sys
import copy
from django.test import TestCase
from rest_framework import status
@@ -11,6 +12,11 @@ from rest_framework.views import APIView
factory = APIRequestFactory()
+if sys.version_info[:2] >= (3, 4):
+ JSON_ERROR = 'JSON parse error - Expecting value:'
+else:
+ JSON_ERROR = 'JSON parse error - No JSON object could be decoded'
+
class BasicView(APIView):
def get(self, request, *args, **kwargs):
@@ -48,7 +54,7 @@ def sanitise_json_error(error_dict):
of json.
"""
ret = copy.copy(error_dict)
- chop = len('JSON parse error - No JSON object could be decoded')
+ chop = len(JSON_ERROR)
ret['detail'] = ret['detail'][:chop]
return ret
@@ -61,7 +67,7 @@ class ClassBasedViewIntegrationTests(TestCase):
request = factory.post('/', 'f00bar', content_type='application/json')
response = self.view(request)
expected = {
- 'detail': 'JSON parse error - No JSON object could be decoded'
+ 'detail': JSON_ERROR
}
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(sanitise_json_error(response.data), expected)
@@ -76,7 +82,7 @@ class ClassBasedViewIntegrationTests(TestCase):
request = factory.post('/', form_data)
response = self.view(request)
expected = {
- 'detail': 'JSON parse error - No JSON object could be decoded'
+ 'detail': JSON_ERROR
}
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(sanitise_json_error(response.data), expected)
@@ -90,7 +96,7 @@ class FunctionBasedViewIntegrationTests(TestCase):
request = factory.post('/', 'f00bar', content_type='application/json')
response = self.view(request)
expected = {
- 'detail': 'JSON parse error - No JSON object could be decoded'
+ 'detail': JSON_ERROR
}
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(sanitise_json_error(response.data), expected)
@@ -105,7 +111,7 @@ class FunctionBasedViewIntegrationTests(TestCase):
request = factory.post('/', form_data)
response = self.view(request)
expected = {
- 'detail': 'JSON parse error - No JSON object could be decoded'
+ 'detail': JSON_ERROR
}
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(sanitise_json_error(response.data), expected)
--
cgit v1.2.3
From 5c12b0768166376783d62632e562f0c1301ee847 Mon Sep 17 00:00:00 2001
From: Xavier Ordoquy
Date: Fri, 16 May 2014 19:40:02 +0200
Subject: Added missing import.
---
rest_framework/serializers.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 2a0d5263..6dd09f68 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -21,6 +21,7 @@ from django.core.paginator import Page
from django.db import models
from django.forms import widgets
from django.utils.datastructures import SortedDict
+from django.core.exceptions import ObjectDoesNotExist
from rest_framework.compat import get_concrete_model, six
from rest_framework.settings import api_settings
--
cgit v1.2.3
From a2e1024f8b0447a712d1f486172d38cfe56535fe Mon Sep 17 00:00:00 2001
From: Xavier Ordoquy
Date: Sun, 18 May 2014 09:27:23 +0200
Subject: Updated Django versions.
---
.travis.yml | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 0c9b4455..638d1499 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,10 +8,10 @@ python:
- "3.4"
env:
- - DJANGO="https://www.djangoproject.com/download/1.7b2/tarball/"
- - DJANGO="django==1.6.3"
- - DJANGO="django==1.5.6"
- - DJANGO="django==1.4.11"
+ - DJANGO="https://www.djangoproject.com/download/1.7b4/tarball/"
+ - DJANGO="django==1.6.5"
+ - DJANGO="django==1.5.8"
+ - DJANGO="django==1.4.13"
- DJANGO="django==1.3.7"
install:
@@ -24,7 +24,7 @@ install:
- "if [[ ${DJANGO::11} == 'django==1.3' ]]; then pip install django-filter==0.5.4; fi"
- "if [[ ${DJANGO::11} != 'django==1.3' ]]; then pip install django-filter==0.7; fi"
- "if [[ ${TRAVIS_PYTHON_VERSION::1} == '3' ]]; then pip install -e git+https://github.com/linovia/django-guardian.git@feature/django_1_7#egg=django-guardian-1.2.0; fi"
- - "if [[ ${DJANGO} == 'https://www.djangoproject.com/download/1.7b2/tarball/' ]]; then pip install -e git+https://github.com/linovia/django-guardian.git@feature/django_1_7#egg=django-guardian-1.2.0; fi"
+ - "if [[ ${DJANGO} == 'https://www.djangoproject.com/download/1.7b4/tarball/' ]]; then pip install -e git+https://github.com/linovia/django-guardian.git@feature/django_1_7#egg=django-guardian-1.2.0; fi"
- export PYTHONPATH=.
script:
@@ -33,16 +33,16 @@ script:
matrix:
exclude:
- python: "2.6"
- env: DJANGO="https://www.djangoproject.com/download/1.7b2/tarball/"
+ env: DJANGO="https://www.djangoproject.com/download/1.7b4/tarball/"
- python: "3.2"
- env: DJANGO="django==1.4.11"
+ env: DJANGO="django==1.4.13"
- python: "3.2"
env: DJANGO="django==1.3.7"
- python: "3.3"
- env: DJANGO="django==1.4.11"
+ env: DJANGO="django==1.4.13"
- python: "3.3"
env: DJANGO="django==1.3.7"
- python: "3.4"
- env: DJANGO="django==1.4.11"
+ env: DJANGO="django==1.4.13"
- python: "3.4"
env: DJANGO="django==1.3.7"
--
cgit v1.2.3
From af1ee3e63175d2b1fd30ab18091bed1019ac5de6 Mon Sep 17 00:00:00 2001
From: Xavier Ordoquy
Date: Sun, 18 May 2014 09:38:46 +0200
Subject: Fixed a small change in the 1.7 beta url.
---
.travis.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 638d1499..b2da9e81 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,7 +8,7 @@ python:
- "3.4"
env:
- - DJANGO="https://www.djangoproject.com/download/1.7b4/tarball/"
+ - DJANGO="https://www.djangoproject.com/download/1.7.b4/tarball/"
- DJANGO="django==1.6.5"
- DJANGO="django==1.5.8"
- DJANGO="django==1.4.13"
@@ -24,7 +24,7 @@ install:
- "if [[ ${DJANGO::11} == 'django==1.3' ]]; then pip install django-filter==0.5.4; fi"
- "if [[ ${DJANGO::11} != 'django==1.3' ]]; then pip install django-filter==0.7; fi"
- "if [[ ${TRAVIS_PYTHON_VERSION::1} == '3' ]]; then pip install -e git+https://github.com/linovia/django-guardian.git@feature/django_1_7#egg=django-guardian-1.2.0; fi"
- - "if [[ ${DJANGO} == 'https://www.djangoproject.com/download/1.7b4/tarball/' ]]; then pip install -e git+https://github.com/linovia/django-guardian.git@feature/django_1_7#egg=django-guardian-1.2.0; fi"
+ - "if [[ ${DJANGO} == 'https://www.djangoproject.com/download/1.7.b4/tarball/' ]]; then pip install -e git+https://github.com/linovia/django-guardian.git@feature/django_1_7#egg=django-guardian-1.2.0; fi"
- export PYTHONPATH=.
script:
@@ -33,7 +33,7 @@ script:
matrix:
exclude:
- python: "2.6"
- env: DJANGO="https://www.djangoproject.com/download/1.7b4/tarball/"
+ env: DJANGO="https://www.djangoproject.com/download/1.7.b4/tarball/"
- python: "3.2"
env: DJANGO="django==1.4.13"
- python: "3.2"
--
cgit v1.2.3
From a1a3ad763996b9ab5535bc5d442c2d6fab10b7cc Mon Sep 17 00:00:00 2001
From: allenhu
Date: Sat, 17 May 2014 06:05:33 +0800
Subject: fix pep8
---
rest_framework/serializers.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 6dd09f68..87d20cfc 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -33,8 +33,8 @@ from rest_framework.settings import api_settings
# This helps keep the separation between model fields, form fields, and
# serializer fields more explicit.
-from rest_framework.relations import *
-from rest_framework.fields import *
+from rest_framework.relations import * # NOQA
+from rest_framework.fields import * # NOQA
def _resolve_model(obj):
@@ -345,7 +345,7 @@ class BaseSerializer(WritableField):
for field_name, field in self.fields.items():
if field.read_only and obj is None:
- continue
+ continue
field.initialize(parent=self, field_name=field_name)
key = self.get_field_key(field_name)
value = field.field_to_native(obj, field_name)
--
cgit v1.2.3
From 1e7b5fd2c04e587e30cf29e15ca3074b8d33b92e Mon Sep 17 00:00:00 2001
From: Ian Foote
Date: Tue, 20 May 2014 14:55:00 +0100
Subject: Document ChoiceField blank_display_value parameter
---
docs/api-guide/fields.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md
index 67fa65d2..58dbf977 100644
--- a/docs/api-guide/fields.md
+++ b/docs/api-guide/fields.md
@@ -184,7 +184,9 @@ Corresponds to `django.db.models.fields.SlugField`.
## ChoiceField
-A field that can accept a value out of a limited set of choices.
+A field that can accept a value out of a limited set of choices. Optionally takes a `blank_display_value` parameter that customizes the display value of an empty choice.
+
+**Signature:** `ChoiceField(choices=(), blank_display_value=None)`
## EmailField
--
cgit v1.2.3
From 04c820b8e5e4ae153eacd1cbf19b39286c374e87 Mon Sep 17 00:00:00 2001
From: John Spray
Date: Thu, 22 May 2014 15:24:35 +0100
Subject: fields: allow help_text on SerializerMethodField
...by passing through any extra *args and **kwargs
to the parent constructor.
Previously one couldn't assign help_text to a
SerializerMethodField during construction.
---
rest_framework/fields.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index 2da89550..4ac5285e 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -1027,9 +1027,9 @@ class SerializerMethodField(Field):
A field that gets its value by calling a method on the serializer it's attached to.
"""
- def __init__(self, method_name):
+ def __init__(self, method_name, *args, **kwargs):
self.method_name = method_name
- super(SerializerMethodField, self).__init__()
+ super(SerializerMethodField, self).__init__(*args, **kwargs)
def field_to_native(self, obj, field_name):
value = getattr(self.parent, self.method_name)(obj)
--
cgit v1.2.3
From 807f7a6bb9e36321f3487b5ac31ef5fdc8f4b3fb Mon Sep 17 00:00:00 2001
From: Piper Merriam
Date: Thu, 22 May 2014 13:51:20 -0600
Subject: Fix _resolve_model to work with unicode strings
---
rest_framework/serializers.py | 14 +++++++-------
rest_framework/tests/test_serializers.py | 5 +++++
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 87d20cfc..c2b414d7 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -49,7 +49,7 @@ def _resolve_model(obj):
String representations should have the format:
'appname.ModelName'
"""
- if type(obj) == str and len(obj.split('.')) == 2:
+ if isinstance(obj, six.string_types) and len(obj.split('.')) == 2:
app_name, model_name = obj.split('.')
return models.get_model(app_name, model_name)
elif inspect.isclass(obj) and issubclass(obj, models.Model):
@@ -759,9 +759,9 @@ class ModelSerializer(Serializer):
field.read_only = True
ret[accessor_name] = field
-
+
# Ensure that 'read_only_fields' is an iterable
- assert isinstance(self.opts.read_only_fields, (list, tuple)), '`read_only_fields` must be a list or tuple'
+ assert isinstance(self.opts.read_only_fields, (list, tuple)), '`read_only_fields` must be a list or tuple'
# Add the `read_only` flag to any fields that have been specified
# in the `read_only_fields` option
@@ -776,10 +776,10 @@ class ModelSerializer(Serializer):
"on serializer '%s'." %
(field_name, self.__class__.__name__))
ret[field_name].read_only = True
-
+
# Ensure that 'write_only_fields' is an iterable
- assert isinstance(self.opts.write_only_fields, (list, tuple)), '`write_only_fields` must be a list or tuple'
-
+ assert isinstance(self.opts.write_only_fields, (list, tuple)), '`write_only_fields` must be a list or tuple'
+
for field_name in self.opts.write_only_fields:
assert field_name not in self.base_fields.keys(), (
"field '%s' on serializer '%s' specified in "
@@ -790,7 +790,7 @@ class ModelSerializer(Serializer):
"Non-existant field '%s' specified in `write_only_fields` "
"on serializer '%s'." %
(field_name, self.__class__.__name__))
- ret[field_name].write_only = True
+ ret[field_name].write_only = True
return ret
diff --git a/rest_framework/tests/test_serializers.py b/rest_framework/tests/test_serializers.py
index 082a400c..120510ac 100644
--- a/rest_framework/tests/test_serializers.py
+++ b/rest_framework/tests/test_serializers.py
@@ -3,6 +3,7 @@ from django.test import TestCase
from rest_framework.serializers import _resolve_model
from rest_framework.tests.models import BasicModel
+from rest_framework.compat import six
class ResolveModelTests(TestCase):
@@ -19,6 +20,10 @@ class ResolveModelTests(TestCase):
resolved_model = _resolve_model('tests.BasicModel')
self.assertEqual(resolved_model, BasicModel)
+ def test_resolve_unicode_representation(self):
+ resolved_model = _resolve_model(six.text_type('tests.BasicModel'))
+ self.assertEqual(resolved_model, BasicModel)
+
def test_resolve_non_django_model(self):
with self.assertRaises(ValueError):
_resolve_model(TestCase)
--
cgit v1.2.3
From eab5933070d5df9078a6b88e85ee933cbfa28955 Mon Sep 17 00:00:00 2001
From: khamaileon
Date: Mon, 26 May 2014 18:43:50 +0200
Subject: Add the allow_add_remove parameter to the get_serializer method
---
docs/api-guide/generic-views.md | 2 +-
rest_framework/generics.py | 8 +++++---
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md
index 7d06f246..bb748981 100755
--- a/docs/api-guide/generic-views.md
+++ b/docs/api-guide/generic-views.md
@@ -187,7 +187,7 @@ Remember that the `pre_save()` method is not called by `GenericAPIView` itself,
You won't typically need to override the following methods, although you might need to call into them if you're writing custom views using `GenericAPIView`.
* `get_serializer_context(self)` - Returns a dictionary containing any extra context that should be supplied to the serializer. Defaults to including `'request'`, `'view'` and `'format'` keys.
-* `get_serializer(self, instance=None, data=None, files=None, many=False, partial=False)` - Returns a serializer instance.
+* `get_serializer(self, instance=None, data=None, files=None, many=False, partial=False, allow_add_remove=False)` - Returns a serializer instance.
* `get_pagination_serializer(self, page)` - Returns a serializer instance to use with paginated data.
* `paginate_queryset(self, queryset)` - Paginate a queryset if required, either returning a page object, or `None` if pagination is not configured for this view.
* `filter_queryset(self, queryset)` - Given a queryset, filter it with whichever filter backends are in use, returning a new queryset.
diff --git a/rest_framework/generics.py b/rest_framework/generics.py
index 7bac510f..7fc9db36 100644
--- a/rest_framework/generics.py
+++ b/rest_framework/generics.py
@@ -90,8 +90,8 @@ class GenericAPIView(views.APIView):
'view': self
}
- def get_serializer(self, instance=None, data=None,
- files=None, many=False, partial=False):
+ def get_serializer(self, instance=None, data=None, files=None, many=False,
+ partial=False, allow_add_remove=False):
"""
Return the serializer instance that should be used for validating and
deserializing input, and for serializing output.
@@ -99,7 +99,9 @@ class GenericAPIView(views.APIView):
serializer_class = self.get_serializer_class()
context = self.get_serializer_context()
return serializer_class(instance, data=data, files=files,
- many=many, partial=partial, context=context)
+ many=many, partial=partial,
+ allow_add_remove=allow_add_remove,
+ context=context)
def get_pagination_serializer(self, page):
"""
--
cgit v1.2.3
From a7ff51118f8c8d696219ea7723b283a0ee680457 Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Thu, 29 May 2014 14:33:16 +0100
Subject: Note on configuring TokenAuthentication
---
docs/api-guide/authentication.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md
index 88a7a011..1cb37d67 100755
--- a/docs/api-guide/authentication.md
+++ b/docs/api-guide/authentication.md
@@ -119,7 +119,7 @@ Unauthenticated responses that are denied permission will result in an `HTTP 401
This authentication scheme uses a simple token-based HTTP Authentication scheme. Token authentication is appropriate for client-server setups, such as native desktop and mobile clients.
-To use the `TokenAuthentication` scheme, include `rest_framework.authtoken` in your `INSTALLED_APPS` setting:
+To use the `TokenAuthentication` scheme you'll need to [configure the authentication classes](#setting-the-authentication-scheme) to include `TokenAuthentication`, and additionally include `rest_framework.authtoken` in your `INSTALLED_APPS` setting:
INSTALLED_APPS = (
...
--
cgit v1.2.3
From 6cb6bfae1b83c8682fa3c3d208c732c8ea49606e Mon Sep 17 00:00:00 2001
From: Danilo Bargen
Date: Fri, 30 May 2014 17:53:26 +0200
Subject: Always use specified content type in APIRequestFactory
If `content_type` is specified in the `APIRequestFactory`, always
include it in the request, even if data is empty.
---
rest_framework/test.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rest_framework/test.py b/rest_framework/test.py
index df5a5b3b..284bcee0 100644
--- a/rest_framework/test.py
+++ b/rest_framework/test.py
@@ -36,7 +36,7 @@ class APIRequestFactory(DjangoRequestFactory):
"""
if not data:
- return ('', None)
+ return ('', content_type)
assert format is None or content_type is None, (
'You may not set both `format` and `content_type`.'
--
cgit v1.2.3
From 31f63e1e5502d45f414df400679c238346137b10 Mon Sep 17 00:00:00 2001
From: Rodolfo Carvalho
Date: Mon, 2 Jun 2014 11:06:03 +0200
Subject: Fix typo in docs
---
docs/api-guide/viewsets.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md
index 23b16575..b3085f75 100644
--- a/docs/api-guide/viewsets.md
+++ b/docs/api-guide/viewsets.md
@@ -137,7 +137,7 @@ The `@action` and `@link` decorators can additionally take extra arguments that
def set_password(self, request, pk=None):
...
-The `@action` decorator will route `POST` requests by default, but may also accept other HTTP methods, by using the `method` argument. For example:
+The `@action` decorator will route `POST` requests by default, but may also accept other HTTP methods, by using the `methods` argument. For example:
@action(methods=['POST', 'DELETE'])
def unset_password(self, request, pk=None):
--
cgit v1.2.3
From 08c4594145a7219a14fafc87db0b9d61483d70d0 Mon Sep 17 00:00:00 2001
From: khamaileon
Date: Thu, 5 Jun 2014 12:49:02 +0200
Subject: Replace ChoiceField type_label
---
rest_framework/fields.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index 4ac5285e..86e8fd9d 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -506,7 +506,7 @@ class SlugField(CharField):
class ChoiceField(WritableField):
type_name = 'ChoiceField'
- type_label = 'multiple choice'
+ type_label = 'choice'
form_field_class = forms.ChoiceField
widget = widgets.Select
default_error_messages = {
--
cgit v1.2.3
From e8ec81f5e985f9cc9f524f77ec23013be918b990 Mon Sep 17 00:00:00 2001
From: Xavier Ordoquy
Date: Sun, 8 Jun 2014 09:03:21 +0200
Subject: Fixed #1624 (thanks @abraithwaite)
---
rest_framework/compat.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/rest_framework/compat.py b/rest_framework/compat.py
index d155f554..fdf12448 100644
--- a/rest_framework/compat.py
+++ b/rest_framework/compat.py
@@ -51,6 +51,7 @@ except ImportError:
# guardian is optional
try:
import guardian
+ import guardian.shortcuts # Fixes #1624
except ImportError:
guardian = None
--
cgit v1.2.3
From be84f71bc906c926c9955a4cf47630b24461067d Mon Sep 17 00:00:00 2001
From: Greg Barker
Date: Tue, 10 Jun 2014 15:20:45 -0700
Subject: Fix #1614 - Corrected reference to serializers.CharField
---
docs/api-guide/serializers.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md
index 0044f070..cedf1ff7 100644
--- a/docs/api-guide/serializers.md
+++ b/docs/api-guide/serializers.md
@@ -73,8 +73,8 @@ Sometimes when serializing objects, you may not want to represent everything exa
If you need to customize the serialized value of a particular field, you can do this by creating a `transform_` method. For example if you needed to render some markdown from a text field:
- description = serializers.TextField()
- description_html = serializers.TextField(source='description', read_only=True)
+ description = serializers.CharField()
+ description_html = serializers.CharField(source='description', read_only=True)
def transform_description_html(self, obj, value):
from django.contrib.markup.templatetags.markup import markdown
--
cgit v1.2.3
From 1386767013d044d337b8e08dd2f9b0197197cccf Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Thu, 12 Jun 2014 11:47:26 +0100
Subject: Version 2.3.14
---
docs/api-guide/content-negotiation.md | 2 --
docs/topics/release-notes.md | 40 +++++++++++++++------------
rest_framework/__init__.py | 2 +-
rest_framework/templatetags/rest_framework.py | 4 +--
4 files changed, 25 insertions(+), 23 deletions(-)
diff --git a/docs/api-guide/content-negotiation.md b/docs/api-guide/content-negotiation.md
index 94dd59ca..58b2a2ce 100644
--- a/docs/api-guide/content-negotiation.md
+++ b/docs/api-guide/content-negotiation.md
@@ -1,5 +1,3 @@
-
-
# Content negotiation
> HTTP has provisions for several mechanisms for "content negotiation" - the process of selecting the best representation for a given response when there are multiple representations available.
diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md
index 335497ee..ea4c912c 100644
--- a/docs/topics/release-notes.md
+++ b/docs/topics/release-notes.md
@@ -40,24 +40,28 @@ You can determine your currently installed version using `pip freeze`:
## 2.3.x series
-### 2.3.x
-
-**Date**: April 2014
-
-* Fix nested serializers linked through a backward foreign key relation
-* Fix bad links for the `BrowsableAPIRenderer` with `YAMLRenderer`
-* Add `UnicodeYAMLRenderer` that extends `YAMLRenderer` with unicode
-* Fix `parse_header` argument convertion
-* Fix mediatype detection under Python3
-* Web browseable API now offers blank option on dropdown when the field is not required
-* `APIException` representation improved for logging purposes
-* Allow source="*" within nested serializers
-* Better support for custom oauth2 provider backends
-* Fix field validation if it's optional and has no value
-* Add `SEARCH_PARAM` and `ORDERING_PARAM`
-* Fix `APIRequestFactory` to support arguments within the url string for GET
-* Allow three transport modes for access tokens when accessing a protected resource
-* Fix `Request`'s `QueryDict` encoding
+### 2.3.14
+
+**Date**: 12th June 2014
+
+* **Security fix**: Escape request path when it is include as part of the login and logout links in the browsable API.
+* `help_text` and `verbose_name` automatically set for related fields on `ModelSerializer`.
+* Fix nested serializers linked through a backward foreign key relation.
+* Fix bad links for the `BrowsableAPIRenderer` with `YAMLRenderer`.
+* Add `UnicodeYAMLRenderer` that extends `YAMLRenderer` with unicode.
+* Fix `parse_header` argument convertion.
+* Fix mediatype detection under Python 3.
+* Web browseable API now offers blank option on dropdown when the field is not required.
+* `APIException` representation improved for logging purposes.
+* Allow source="*" within nested serializers.
+* Better support for custom oauth2 provider backends.
+* Fix field validation if it's optional and has no value.
+* Add `SEARCH_PARAM` and `ORDERING_PARAM`.
+* Fix `APIRequestFactory` to support arguments within the url string for GET.
+* Allow three transport modes for access tokens when accessing a protected resource.
+* Fix `QueryDict` encoding on request objects.
+* Ensure throttle keys do not contain spaces, as those are invalid if using `memcached`.
+* Support `blank_display_value` on `ChoiceField`.
### 2.3.13
diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py
index 2d76b55d..01036cef 100644
--- a/rest_framework/__init__.py
+++ b/rest_framework/__init__.py
@@ -8,7 +8,7 @@ ______ _____ _____ _____ __ _
"""
__title__ = 'Django REST framework'
-__version__ = '2.3.13'
+__version__ = '2.3.14'
__author__ = 'Tom Christie'
__license__ = 'BSD 2-Clause'
__copyright__ = 'Copyright 2011-2014 Tom Christie'
diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py
index dff176d6..a155d8d2 100644
--- a/rest_framework/templatetags/rest_framework.py
+++ b/rest_framework/templatetags/rest_framework.py
@@ -122,7 +122,7 @@ def optional_login(request):
except NoReverseMatch:
return ''
- snippet = "Log in" % (login_url, request.path)
+ snippet = "Log in" % (login_url, escape(request.path))
return snippet
@@ -136,7 +136,7 @@ def optional_logout(request):
except NoReverseMatch:
return ''
- snippet = "Log out" % (logout_url, request.path)
+ snippet = "Log out" % (logout_url, escape(request.path))
return snippet
--
cgit v1.2.3
From 82659873c9d3e3058b7e7ea63e4c4b190c7fc19c Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Thu, 12 Jun 2014 11:48:58 +0100
Subject: Fix accidental docs change
---
docs/api-guide/content-negotiation.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/api-guide/content-negotiation.md b/docs/api-guide/content-negotiation.md
index 58b2a2ce..94dd59ca 100644
--- a/docs/api-guide/content-negotiation.md
+++ b/docs/api-guide/content-negotiation.md
@@ -1,3 +1,5 @@
+
+
# Content negotiation
> HTTP has provisions for several mechanisms for "content negotiation" - the process of selecting the best representation for a given response when there are multiple representations available.
--
cgit v1.2.3
From 544183f64ff115398ae62c916d58f8369263d47c Mon Sep 17 00:00:00 2001
From: TankorSmash
Date: Mon, 16 Jun 2014 19:13:02 -0400
Subject: typo in the docs
---
rest_framework/fields.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index 86e8fd9d..d5c8134b 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -187,7 +187,7 @@ class Field(object):
def field_to_native(self, obj, field_name):
"""
- Given and object and a field name, returns the value that should be
+ Given an object and a field name, returns the value that should be
serialized for that field.
"""
if obj is None:
--
cgit v1.2.3
From c1426d1078a40de521aeec6c4099538efa6b24c7 Mon Sep 17 00:00:00 2001
From: Chibisov Gennady
Date: Thu, 26 Jun 2014 23:29:00 +0400
Subject: Fixes #1651. Add link to drf-extensions nested routers to docs
---
docs/api-guide/routers.md | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md
index 7efc140a..64f05af3 100644
--- a/docs/api-guide/routers.md
+++ b/docs/api-guide/routers.md
@@ -179,7 +179,16 @@ The [wq.db package][wq.db] provides an advanced [Router][wq.db-router] class (an
app.router.register_model(MyModel)
+## DRF-extensions
+
+The [`DRF-extensions` package][drf-extensions] provides [routers][drf-extensions-routers] for creating [nested viewsets][drf-extensions-nested-viewsets], [collection level controllers][drf-extensions-collection-level-controllers] with [customizable endpoint names][drf-extensions-customizable-endpoint-names].
+
[cite]: http://guides.rubyonrails.org/routing.html
[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers
[wq.db]: http://wq.io/wq.db
[wq.db-router]: http://wq.io/docs/app.py
+[drf-extensions]: http://chibisov.github.io/drf-extensions/docs/
+[drf-extensions-routers]: http://chibisov.github.io/drf-extensions/docs/#routers
+[drf-extensions-nested-viewsets]: http://chibisov.github.io/drf-extensions/docs/#nested-routes
+[drf-extensions-collection-level-controllers]: http://chibisov.github.io/drf-extensions/docs/#collection-level-controllers
+[drf-extensions-customizable-endpoint-names]: http://chibisov.github.io/drf-extensions/docs/#controller-endpoint-name
\ No newline at end of file
--
cgit v1.2.3