Commit 2aca69a9 by Xavier Ordoquy

Merge remote-tracking branch 'reference/master' into feature/pytest

Conflicts:
	tests/test_serializer.py
parents c5f68fba d8fb81ce
...@@ -7,10 +7,10 @@ python: ...@@ -7,10 +7,10 @@ python:
- "3.3" - "3.3"
env: env:
- DJANGO="https://www.djangoproject.com/download/1.7b1/tarball/" - DJANGO="https://www.djangoproject.com/download/1.7b2/tarball/"
- DJANGO="django==1.6.2" - DJANGO="django==1.6.3"
- DJANGO="django==1.5.5" - DJANGO="django==1.5.6"
- DJANGO="django==1.4.10" - DJANGO="django==1.4.11"
- DJANGO="django==1.3.7" - DJANGO="django==1.3.7"
install: install:
...@@ -24,7 +24,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.5.4; fi"
- "if [[ ${DJANGO::11} != 'django==1.3' ]]; then pip install django-filter==0.7; 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 [[ ${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.7b1/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.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"
- export PYTHONPATH=. - export PYTHONPATH=.
script: script:
...@@ -33,13 +33,13 @@ script: ...@@ -33,13 +33,13 @@ script:
matrix: matrix:
exclude: exclude:
- python: "2.6" - python: "2.6"
env: DJANGO="https://www.djangoproject.com/download/1.7b1/tarball/" env: DJANGO="https://www.djangoproject.com/download/1.7b2/tarball/"
- python: "3.2" - python: "3.2"
env: DJANGO="django==1.4.10" env: DJANGO="django==1.4.11"
- python: "3.2" - python: "3.2"
env: DJANGO="django==1.3.7" env: DJANGO="django==1.3.7"
- python: "3.3" - python: "3.3"
env: DJANGO="django==1.4.10" env: DJANGO="django==1.4.11"
- python: "3.3" - python: "3.3"
env: DJANGO="django==1.3.7" env: DJANGO="django==1.3.7"
...@@ -70,7 +70,7 @@ The following attributes control the basic view behavior. ...@@ -70,7 +70,7 @@ The following attributes control the basic view behavior.
**Shortcuts**: **Shortcuts**:
* `model` - This shortcut may be used instead of setting either (or both) of the `queryset`/`serializer_class` attributes, although using the explicit style is generally preferred. If used instead of `serializer_class`, then then `DEFAULT_MODEL_SERIALIZER_CLASS` setting will determine the base serializer class. Note that `model` is only ever used for generating a default queryset or serializer class - the `queryset` and `serializer_class` attributes are always preferred if provided. * `model` - This shortcut may be used instead of setting either (or both) of the `queryset`/`serializer_class` attributes, although using the explicit style is generally preferred. If used instead of `serializer_class`, then `DEFAULT_MODEL_SERIALIZER_CLASS` setting will determine the base serializer class. Note that `model` is only ever used for generating a default queryset or serializer class - the `queryset` and `serializer_class` attributes are always preferred if provided.
**Pagination**: **Pagination**:
......
...@@ -103,6 +103,7 @@ You can also set the pagination style on a per-view basis, using the `ListAPIVie ...@@ -103,6 +103,7 @@ You can also set the pagination style on a per-view basis, using the `ListAPIVie
max_paginate_by = 100 max_paginate_by = 100
Note that using a `paginate_by` value of `None` will turn off pagination for the view. Note that using a `paginate_by` value of `None` will turn off pagination for the view.
Note if you use the `PAGINATE_BY_PARAM` settings, you also have to set the `paginate_by_param` attribute in your view to `None` in order to turn off pagination for those requests that contain the `paginate_by_param` parameter.
For more complex requirements such as serialization that differs depending on the requested media type you can override the `.get_paginate_by()` and `.get_pagination_serializer_class()` methods. For more complex requirements such as serialization that differs depending on the requested media type you can override the `.get_paginate_by()` and `.get_pagination_serializer_class()` methods.
...@@ -157,4 +158,4 @@ The [`DRF-extensions` package][drf-extensions] includes a [`PaginateByMaxMixin` ...@@ -157,4 +158,4 @@ The [`DRF-extensions` package][drf-extensions] includes a [`PaginateByMaxMixin`
[cite]: https://docs.djangoproject.com/en/dev/topics/pagination/ [cite]: https://docs.djangoproject.com/en/dev/topics/pagination/
[drf-extensions]: http://chibisov.github.io/drf-extensions/docs/ [drf-extensions]: http://chibisov.github.io/drf-extensions/docs/
[paginate-by-max-mixin]: http://chibisov.github.io/drf-extensions/docs/#paginatebymaxmixin [paginate-by-max-mixin]: http://chibisov.github.io/drf-extensions/docs/#paginatebymaxmixin
\ No newline at end of file
...@@ -56,7 +56,7 @@ You can also set the authentication policy on a per-view, or per-viewset basis, ...@@ -56,7 +56,7 @@ You can also set the authentication policy on a per-view, or per-viewset basis,
using the `APIView` class based views. using the `APIView` class based views.
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.responses import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
class ExampleView(APIView): class ExampleView(APIView):
......
...@@ -40,6 +40,25 @@ You can determine your currently installed version using `pip freeze`: ...@@ -40,6 +40,25 @@ You can determine your currently installed version using `pip freeze`:
## 2.3.x series ## 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.13 ### 2.3.13
**Date**: 6th March 2014 **Date**: 6th March 2014
......
...@@ -34,7 +34,7 @@ class Token(models.Model): ...@@ -34,7 +34,7 @@ class Token(models.Model):
return super(Token, self).save(*args, **kwargs) return super(Token, self).save(*args, **kwargs)
def generate_key(self): def generate_key(self):
return binascii.hexlify(os.urandom(20)) return binascii.hexlify(os.urandom(20)).decode()
def __unicode__(self): def __unicode__(self):
return self.key return self.key
...@@ -289,7 +289,7 @@ class WritableField(Field): ...@@ -289,7 +289,7 @@ class WritableField(Field):
self.validators = self.default_validators + validators self.validators = self.default_validators + validators
self.default = default if default is not None else self.default self.default = default if default is not None else self.default
# Widgets are ony used for HTML forms. # Widgets are only used for HTML forms.
widget = widget or self.widget widget = widget or self.widget
if isinstance(widget, type): if isinstance(widget, type):
widget = widget() widget = widget()
......
...@@ -828,6 +828,10 @@ class ModelSerializer(Serializer): ...@@ -828,6 +828,10 @@ class ModelSerializer(Serializer):
if model_field: if model_field:
kwargs['required'] = not(model_field.null or model_field.blank) kwargs['required'] = not(model_field.null or model_field.blank)
if model_field.help_text is not None:
kwargs['help_text'] = model_field.help_text
if model_field.verbose_name is not None:
kwargs['label'] = model_field.verbose_name
return PrimaryKeyRelatedField(**kwargs) return PrimaryKeyRelatedField(**kwargs)
...@@ -1088,6 +1092,10 @@ class HyperlinkedModelSerializer(ModelSerializer): ...@@ -1088,6 +1092,10 @@ class HyperlinkedModelSerializer(ModelSerializer):
if model_field: if model_field:
kwargs['required'] = not(model_field.null or model_field.blank) kwargs['required'] = not(model_field.null or model_field.blank)
if model_field.help_text is not None:
kwargs['help_text'] = model_field.help_text
if model_field.verbose_name is not None:
kwargs['label'] = model_field.verbose_name
if self.opts.lookup_field: if self.opts.lookup_field:
kwargs['lookup_field'] = self.opts.lookup_field kwargs['lookup_field'] = self.opts.lookup_field
......
...@@ -143,7 +143,8 @@ class ForeignKeyTarget(RESTFrameworkModel): ...@@ -143,7 +143,8 @@ class ForeignKeyTarget(RESTFrameworkModel):
class ForeignKeySource(RESTFrameworkModel): class ForeignKeySource(RESTFrameworkModel):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
target = models.ForeignKey(ForeignKeyTarget, related_name='sources') target = models.ForeignKey(ForeignKeyTarget, related_name='sources',
help_text='Target', verbose_name='Target')
# Nullable ForeignKey # Nullable ForeignKey
......
...@@ -19,7 +19,7 @@ from rest_framework.authentication import ( ...@@ -19,7 +19,7 @@ from rest_framework.authentication import (
OAuth2Authentication OAuth2Authentication
) )
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
from rest_framework.compat import patterns, url, include from rest_framework.compat import patterns, url, include, six
from rest_framework.compat import oauth2_provider, oauth2_provider_scope from rest_framework.compat import oauth2_provider, oauth2_provider_scope
from rest_framework.compat import oauth, oauth_provider from rest_framework.compat import oauth, oauth_provider
from rest_framework.test import APIRequestFactory, APIClient from rest_framework.test import APIRequestFactory, APIClient
...@@ -195,6 +195,12 @@ class TokenAuthTests(TestCase): ...@@ -195,6 +195,12 @@ class TokenAuthTests(TestCase):
token = Token.objects.create(user=self.user) token = Token.objects.create(user=self.user)
self.assertTrue(bool(token.key)) self.assertTrue(bool(token.key))
def test_generate_key_returns_string(self):
"""Ensure generate_key returns a string"""
token = Token()
key = token.generate_key()
self.assertTrue(isinstance(key, six.string_types))
def test_token_login_json(self): def test_token_login_json(self):
"""Ensure token login view using JSON POST works.""" """Ensure token login view using JSON POST works."""
client = APIClient(enforce_csrf_checks=True) client = APIClient(enforce_csrf_checks=True)
......
...@@ -9,7 +9,8 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -9,7 +9,8 @@ from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers, fields, relations from rest_framework import serializers, fields, relations
from tests.models import (HasPositiveIntegerAsChoice, Album, ActionItem, Anchor, BasicModel, from tests.models import (HasPositiveIntegerAsChoice, Album, ActionItem, Anchor, BasicModel,
BlankFieldModel, BlogPost, BlogPostComment, Book, CallableDefaultValueModel, DefaultValueModel, BlankFieldModel, BlogPost, BlogPostComment, Book, CallableDefaultValueModel, DefaultValueModel,
ManyToManyModel, Person, ReadOnlyManyToManyModel, Photo, RESTFrameworkModel) ManyToManyModel, Person, ReadOnlyManyToManyModel, Photo, RESTFrameworkModel,
ForeignKeySource, ManyToManySource)
from tests.models import BasicModelSerializer from tests.models import BasicModelSerializer
import datetime import datetime
import pickle import pickle
...@@ -176,6 +177,16 @@ class PositiveIntegerAsChoiceSerializer(serializers.ModelSerializer): ...@@ -176,6 +177,16 @@ class PositiveIntegerAsChoiceSerializer(serializers.ModelSerializer):
fields = ['some_integer'] fields = ['some_integer']
class ForeignKeySourceSerializer(serializers.ModelSerializer):
class Meta:
model = ForeignKeySource
class HyperlinkedForeignKeySourceSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = ForeignKeySource
class BasicTests(TestCase): class BasicTests(TestCase):
def setUp(self): def setUp(self):
self.comment = Comment( self.comment = Comment(
...@@ -1600,6 +1611,19 @@ class ManyFieldHelpTextTest(TestCase): ...@@ -1600,6 +1611,19 @@ class ManyFieldHelpTextTest(TestCase):
self.assertEqual('Some help text.', rel_field.help_text) self.assertEqual('Some help text.', rel_field.help_text)
class AttributeMappingOnAutogeneratedRelatedFields(TestCase):
def test_primary_key_related_field(self):
serializer = ForeignKeySourceSerializer()
self.assertEqual(serializer.fields['target'].help_text, 'Target')
self.assertEqual(serializer.fields['target'].label, 'Target')
def test_hyperlinked_related_field(self):
serializer = HyperlinkedForeignKeySourceSerializer()
self.assertEqual(serializer.fields['target'].help_text, 'Target')
self.assertEqual(serializer.fields['target'].label, 'Target')
@unittest.skipUnless(PIL is not None, 'PIL is not installed') @unittest.skipUnless(PIL is not None, 'PIL is not installed')
class AttributeMappingOnAutogeneratedFieldsTests(TestCase): class AttributeMappingOnAutogeneratedFieldsTests(TestCase):
......
...@@ -7,7 +7,7 @@ commands = py.test -q ...@@ -7,7 +7,7 @@ commands = py.test -q
[testenv:py3.3-django1.7] [testenv:py3.3-django1.7]
basepython = python3.3 basepython = python3.3
deps = https://www.djangoproject.com/download/1.7b1/tarball/ deps = https://www.djangoproject.com/download/1.7b2/tarball/
django-filter==0.7 django-filter==0.7
defusedxml==0.3 defusedxml==0.3
Pillow==2.3.0 Pillow==2.3.0
...@@ -15,7 +15,7 @@ deps = https://www.djangoproject.com/download/1.7b1/tarball/ ...@@ -15,7 +15,7 @@ deps = https://www.djangoproject.com/download/1.7b1/tarball/
[testenv:py3.2-django1.7] [testenv:py3.2-django1.7]
basepython = python3.2 basepython = python3.2
deps = https://www.djangoproject.com/download/1.7b1/tarball/ deps = https://www.djangoproject.com/download/1.7b2/tarball/
django-filter==0.7 django-filter==0.7
defusedxml==0.3 defusedxml==0.3
Pillow==2.3.0 Pillow==2.3.0
...@@ -23,7 +23,7 @@ deps = https://www.djangoproject.com/download/1.7b1/tarball/ ...@@ -23,7 +23,7 @@ deps = https://www.djangoproject.com/download/1.7b1/tarball/
[testenv:py2.7-django1.7] [testenv:py2.7-django1.7]
basepython = python2.7 basepython = python2.7
deps = https://www.djangoproject.com/download/1.7b1/tarball/ deps = https://www.djangoproject.com/download/1.7b2/tarball/
django-filter==0.7 django-filter==0.7
defusedxml==0.3 defusedxml==0.3
django-oauth-plus==2.2.1 django-oauth-plus==2.2.1
...@@ -35,7 +35,7 @@ deps = https://www.djangoproject.com/download/1.7b1/tarball/ ...@@ -35,7 +35,7 @@ deps = https://www.djangoproject.com/download/1.7b1/tarball/
[testenv:py3.3-django1.6] [testenv:py3.3-django1.6]
basepython = python3.3 basepython = python3.3
deps = Django==1.6 deps = Django==1.6.3
django-filter==0.7 django-filter==0.7
defusedxml==0.3 defusedxml==0.3
Pillow==2.3.0 Pillow==2.3.0
...@@ -43,7 +43,7 @@ deps = Django==1.6 ...@@ -43,7 +43,7 @@ deps = Django==1.6
[testenv:py3.2-django1.6] [testenv:py3.2-django1.6]
basepython = python3.2 basepython = python3.2
deps = Django==1.6 deps = Django==1.6.3
django-filter==0.7 django-filter==0.7
defusedxml==0.3 defusedxml==0.3
Pillow==2.3.0 Pillow==2.3.0
...@@ -51,7 +51,7 @@ deps = Django==1.6 ...@@ -51,7 +51,7 @@ deps = Django==1.6
[testenv:py2.7-django1.6] [testenv:py2.7-django1.6]
basepython = python2.7 basepython = python2.7
deps = Django==1.6 deps = Django==1.6.3
django-filter==0.7 django-filter==0.7
defusedxml==0.3 defusedxml==0.3
django-oauth-plus==2.2.1 django-oauth-plus==2.2.1
...@@ -63,7 +63,7 @@ deps = Django==1.6 ...@@ -63,7 +63,7 @@ deps = Django==1.6
[testenv:py2.6-django1.6] [testenv:py2.6-django1.6]
basepython = python2.6 basepython = python2.6
deps = Django==1.6 deps = Django==1.6.3
django-filter==0.7 django-filter==0.7
defusedxml==0.3 defusedxml==0.3
django-oauth-plus==2.2.1 django-oauth-plus==2.2.1
...@@ -75,7 +75,7 @@ deps = Django==1.6 ...@@ -75,7 +75,7 @@ deps = Django==1.6
[testenv:py3.3-django1.5] [testenv:py3.3-django1.5]
basepython = python3.3 basepython = python3.3
deps = django==1.5.5 deps = django==1.5.6
django-filter==0.7 django-filter==0.7
defusedxml==0.3 defusedxml==0.3
Pillow==2.3.0 Pillow==2.3.0
...@@ -83,7 +83,7 @@ deps = django==1.5.5 ...@@ -83,7 +83,7 @@ deps = django==1.5.5
[testenv:py3.2-django1.5] [testenv:py3.2-django1.5]
basepython = python3.2 basepython = python3.2
deps = django==1.5.5 deps = django==1.5.6
django-filter==0.7 django-filter==0.7
defusedxml==0.3 defusedxml==0.3
Pillow==2.3.0 Pillow==2.3.0
...@@ -91,7 +91,7 @@ deps = django==1.5.5 ...@@ -91,7 +91,7 @@ deps = django==1.5.5
[testenv:py2.7-django1.5] [testenv:py2.7-django1.5]
basepython = python2.7 basepython = python2.7
deps = django==1.5.5 deps = django==1.5.6
django-filter==0.7 django-filter==0.7
defusedxml==0.3 defusedxml==0.3
django-oauth-plus==2.2.1 django-oauth-plus==2.2.1
...@@ -103,7 +103,7 @@ deps = django==1.5.5 ...@@ -103,7 +103,7 @@ deps = django==1.5.5
[testenv:py2.6-django1.5] [testenv:py2.6-django1.5]
basepython = python2.6 basepython = python2.6
deps = django==1.5.5 deps = django==1.5.6
django-filter==0.7 django-filter==0.7
defusedxml==0.3 defusedxml==0.3
django-oauth-plus==2.2.1 django-oauth-plus==2.2.1
...@@ -115,7 +115,7 @@ deps = django==1.5.5 ...@@ -115,7 +115,7 @@ deps = django==1.5.5
[testenv:py2.7-django1.4] [testenv:py2.7-django1.4]
basepython = python2.7 basepython = python2.7
deps = django==1.4.10 deps = django==1.4.11
django-filter==0.7 django-filter==0.7
defusedxml==0.3 defusedxml==0.3
django-oauth-plus==2.2.1 django-oauth-plus==2.2.1
...@@ -127,7 +127,7 @@ deps = django==1.4.10 ...@@ -127,7 +127,7 @@ deps = django==1.4.10
[testenv:py2.6-django1.4] [testenv:py2.6-django1.4]
basepython = python2.6 basepython = python2.6
deps = django==1.4.10 deps = django==1.4.11
django-filter==0.7 django-filter==0.7
defusedxml==0.3 defusedxml==0.3
django-oauth-plus==2.2.1 django-oauth-plus==2.2.1
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment