From d48ba1cff76ffceb1d700e9e0c6ccf518a6382da Mon Sep 17 00:00:00 2001
From: Andrey Kaygorodov
Date: Wed, 5 Feb 2014 05:47:27 +0800
Subject: turn of pagination
---
 docs/api-guide/pagination.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md
index 0829589f..f86e6ce1 100644
--- a/docs/api-guide/pagination.md
+++ b/docs/api-guide/pagination.md
@@ -102,7 +102,7 @@ You can also set the pagination style on a per-view basis, using the `ListAPIVie
         paginate_by_param = 'page_size'
         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. But if you specified `PAGINATE_BY` and `PAGINATE_BY_PARAM` in your settings file then you have to set both `paginate_by` and `paginate_by_param` to a `None` value in order to turn off pagination for the view.
 
 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.
 
-- 
cgit v1.2.3
From 2d20512d259f51a5a5c2b71b20f98d24e0176f16 Mon Sep 17 00:00:00 2001
From: Andrey Kaygorodov
Date: Wed, 5 Feb 2014 21:10:51 +0800
Subject: #1390, docs, turning of pagination
---
 docs/api-guide/pagination.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md
index f86e6ce1..047a0988 100644
--- a/docs/api-guide/pagination.md
+++ b/docs/api-guide/pagination.md
@@ -102,7 +102,8 @@ You can also set the pagination style on a per-view basis, using the `ListAPIVie
         paginate_by_param = 'page_size'
         max_paginate_by = 100
 
-Note that using a `paginate_by` value of `None` will turn off pagination for the view. But if you specified `PAGINATE_BY` and `PAGINATE_BY_PARAM` in your settings file then you have to set both `paginate_by` and `paginate_by_param` to a `None` value in order to 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.
 
-- 
cgit v1.2.3
From a23059b6f73aaff9709f611826bac892e56663dd Mon Sep 17 00:00:00 2001
From: Miro Hrončok
Date: Wed, 9 Apr 2014 23:35:41 +0200
Subject: Add more TRAILING_PUNCTUATION to work with YAML.
Fixes #1517---
 rest_framework/templatetags/rest_framework.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py
index beb8c5b0..dff176d6 100644
--- a/rest_framework/templatetags/rest_framework.py
+++ b/rest_framework/templatetags/rest_framework.py
@@ -180,7 +180,7 @@ def add_class(value, css_class):
 
 
 # Bunch of stuff cloned from urlize
-TRAILING_PUNCTUATION = ['.', ',', ':', ';', '.)', '"', "'"]
+TRAILING_PUNCTUATION = ['.', ',', ':', ';', '.)', '"', "']", "'}", "'"]
 WRAPPING_PUNCTUATION = [('(', ')'), ('<', '>'), ('[', ']'), ('<', '>'),
                         ('"', '"'), ("'", "'")]
 word_split_re = re.compile(r'(\s+)')
-- 
cgit v1.2.3
From 7ae8409370635ccec7d3c160ea87281f21c9ae11 Mon Sep 17 00:00:00 2001
From: Miro Hrončok
Date: Thu, 10 Apr 2014 01:35:45 +0200
Subject: Allow unicode YAML dump with UnicodeYAMLRenderer
Fixes #1519
---
 rest_framework/renderers.py | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py
index 7a7da561..484961ad 100644
--- a/rest_framework/renderers.py
+++ b/rest_framework/renderers.py
@@ -193,6 +193,7 @@ class YAMLRenderer(BaseRenderer):
     format = 'yaml'
     encoder = encoders.SafeDumper
     charset = 'utf-8'
+    ensure_ascii = True
 
     def render(self, data, accepted_media_type=None, renderer_context=None):
         """
@@ -203,7 +204,15 @@ class YAMLRenderer(BaseRenderer):
         if data is None:
             return ''
 
-        return yaml.dump(data, stream=None, encoding=self.charset, Dumper=self.encoder)
+        return yaml.dump(data, stream=None, encoding=self.charset, Dumper=self.encoder, allow_unicode=not self.ensure_ascii)
+
+
+class UnicodeYAMLRenderer(YAMLRenderer):
+    """
+    Renderer which serializes to YAML.
+    Does *not* apply character escaping for non-ascii characters.
+    """
+    ensure_ascii = False
 
 
 class TemplateHTMLRenderer(BaseRenderer):
-- 
cgit v1.2.3
From f68596a7326777f971d9551ff1bfc7176389ea22 Mon Sep 17 00:00:00 2001
From: Miro Hrončok
Date: Thu, 10 Apr 2014 01:58:06 +0200
Subject: Document new UnicodeYAMLRenderer
---
 docs/api-guide/renderers.md | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md
index 7798827b..7a3429bf 100644
--- a/docs/api-guide/renderers.md
+++ b/docs/api-guide/renderers.md
@@ -138,6 +138,26 @@ Renders the request data into `YAML`.
 
 Requires the `pyyaml` package to be installed.
 
+Note that non-ascii characters will be rendered using `\uXXXX` character escape.  For example:
+
+    unicode black star: "\u2605"
+
+**.media_type**: `application/yaml`
+
+**.format**: `'.yaml'`
+
+**.charset**: `utf-8`
+
+## UnicodeYAMLRenderer
+
+Renders the request data into `YAML`. 
+
+Requires the `pyyaml` package to be installed.
+
+Note that non-ascii characters will not be character escaped.  For example:
+
+    unicode black star: ★
+
 **.media_type**: `application/yaml`
 
 **.format**: `'.yaml'`
-- 
cgit v1.2.3
From 0a0e4f22e72badd1d8700a2b253cb27450a5319f Mon Sep 17 00:00:00 2001
From: Ian Foote
Date: Sat, 12 Apr 2014 17:51:02 +0100
Subject: Set GenericForeignKey fields on object before save
* A model with a required GenericForeignKey can be saved if the field is set
---
 rest_framework/serializers.py | 3 +++
 1 file changed, 3 insertions(+)
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index cb7539e0..1d6097ed 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -16,6 +16,7 @@ import datetime
 import inspect
 import types
 from decimal import Decimal
+from django.contrib.contenttypes.generic import GenericForeignKey
 from django.core.paginator import Page
 from django.db import models
 from django.forms import widgets
@@ -943,6 +944,8 @@ class ModelSerializer(Serializer):
 
         # Forward m2m relations
         for field in meta.many_to_many + meta.virtual_fields:
+            if isinstance(field, GenericForeignKey):
+                continue
             if field.name in attrs:
                 m2m_data[field.name] = attrs.pop(field.name)
 
-- 
cgit v1.2.3
From 853c7a16c15c7291561bc4b3dfbcad88ea262a18 Mon Sep 17 00:00:00 2001
From: Ian Foote
Date: Sun, 13 Apr 2014 17:26:15 +0100
Subject: Use setattr for adding fields to a new instance
Add test for restoring a GenericForeignKey
---
 rest_framework/serializers.py                 | 18 ++++++++----------
 rest_framework/tests/test_genericrelations.py | 18 ++++++++++++++++++
 2 files changed, 26 insertions(+), 10 deletions(-)
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 1d6097ed..ea9509bf 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -955,17 +955,15 @@ class ModelSerializer(Serializer):
             if isinstance(self.fields.get(field_name, None), Serializer):
                 nested_forward_relations[field_name] = attrs[field_name]
 
-        # Update an existing instance...
-        if instance is not None:
-            for key, val in attrs.items():
-                try:
-                    setattr(instance, key, val)
-                except ValueError:
-                    self._errors[key] = self.error_messages['required']
+        # Create an empty instance of the model
+        if instance is None:
+            instance = self.opts.model()
 
-        # ...or create a new instance
-        else:
-            instance = self.opts.model(**attrs)
+        for key, val in attrs.items():
+            try:
+                setattr(instance, key, val)
+            except ValueError:
+                self._errors[key] = self.error_messages['required']
 
         # Any relations that cannot be set until we've
         # saved the model get hidden away on these
diff --git a/rest_framework/tests/test_genericrelations.py b/rest_framework/tests/test_genericrelations.py
index fa09c9e6..46a2d863 100644
--- a/rest_framework/tests/test_genericrelations.py
+++ b/rest_framework/tests/test_genericrelations.py
@@ -131,3 +131,21 @@ class TestGenericRelations(TestCase):
         }
         ]
         self.assertEqual(serializer.data, expected)
+
+    def test_restore_object_generic_fk(self):
+        """
+        Ensure an object with a generic foreign key can be restored.
+        """
+
+        class TagSerializer(serializers.ModelSerializer):
+            class Meta:
+                model = Tag
+                exclude = ('content_type', 'object_id')
+
+        serializer = TagSerializer()
+
+        bookmark = Bookmark(url='http://example.com')
+        attrs = {'tagged_item': bookmark, 'tag': 'example'}
+
+        tag = serializer.restore_object(attrs)
+        self.assertEqual(tag.tagged_item, bookmark)
-- 
cgit v1.2.3
From 4b3eb6e0b0e6412693de126ac92482a276ca9a78 Mon Sep 17 00:00:00 2001
From: Vladislav Vlastovskiy
Date: Mon, 14 Apr 2014 12:21:38 +0400
Subject: Fixed parse file name
---
 rest_framework/parsers.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py
index f1b3e38d..703cefca 100644
--- a/rest_framework/parsers.py
+++ b/rest_framework/parsers.py
@@ -288,7 +288,7 @@ class FileUploadParser(BaseParser):
 
         try:
             meta = parser_context['request'].META
-            disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'])
+            disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode('utf-8'))
             return disposition[1]['filename']
         except (AttributeError, KeyError):
             pass
-- 
cgit v1.2.3
From 063addabfeb716f54c5784917e92ab6abb635ff5 Mon Sep 17 00:00:00 2001
From: Vladislav Vlastovskiy
Date: Mon, 14 Apr 2014 12:28:41 +0400
Subject: Removed encode from test
Django does not produce such a decoding by default, this test was not honest.---
 rest_framework/tests/test_parsers.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rest_framework/tests/test_parsers.py b/rest_framework/tests/test_parsers.py
index 7699e10c..ffd6b360 100644
--- a/rest_framework/tests/test_parsers.py
+++ b/rest_framework/tests/test_parsers.py
@@ -96,7 +96,7 @@ class TestFileUploadParser(TestCase):
         request = MockRequest()
         request.upload_handlers = (MemoryFileUploadHandler(),)
         request.META = {
-            'HTTP_CONTENT_DISPOSITION': 'Content-Disposition: inline; filename=file.txt'.encode('utf-8'),
+            'HTTP_CONTENT_DISPOSITION': 'Content-Disposition: inline; filename=file.txt',
             'HTTP_CONTENT_LENGTH': 14,
         }
         self.parser_context = {'request': request, 'kwargs': {}}
-- 
cgit v1.2.3
From d474934d365291c28d5741898257cbdd5d0aa9ec Mon Sep 17 00:00:00 2001
From: Vladislav Vlastovskiy
Date: Mon, 14 Apr 2014 13:01:24 +0400
Subject: Fixed return type
From bytes to str---
 rest_framework/parsers.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py
index 703cefca..d49b17a4 100644
--- a/rest_framework/parsers.py
+++ b/rest_framework/parsers.py
@@ -10,6 +10,7 @@ from django.core.files.uploadhandler import StopFutureHandlers
 from django.http import QueryDict
 from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser
 from django.http.multipartparser import MultiPartParserError, parse_header, ChunkIter
+from django.utils.encoding import force_str
 from rest_framework.compat import etree, six, yaml
 from rest_framework.exceptions import ParseError
 from rest_framework import renderers
@@ -289,6 +290,6 @@ class FileUploadParser(BaseParser):
         try:
             meta = parser_context['request'].META
             disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode('utf-8'))
-            return disposition[1]['filename']
+            return force_str(disposition[1]['filename'])
         except (AttributeError, KeyError):
             pass
-- 
cgit v1.2.3
From d1f4dfca2061cb552158ac7ea6f2de609989797b Mon Sep 17 00:00:00 2001
From: Vladislav Vlastovskiy
Date: Mon, 14 Apr 2014 13:04:18 +0400
Subject: Removed decode from test filename
---
 rest_framework/tests/test_parsers.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rest_framework/tests/test_parsers.py b/rest_framework/tests/test_parsers.py
index ffd6b360..8af90677 100644
--- a/rest_framework/tests/test_parsers.py
+++ b/rest_framework/tests/test_parsers.py
@@ -112,4 +112,4 @@ class TestFileUploadParser(TestCase):
     def test_get_filename(self):
         parser = FileUploadParser()
         filename = parser.get_filename(self.stream, None, self.parser_context)
-        self.assertEqual(filename, 'file.txt'.encode('utf-8'))
+        self.assertEqual(filename, 'file.txt')
-- 
cgit v1.2.3
From 3fe038357267f947eba467f2b7714a782fa93c33 Mon Sep 17 00:00:00 2001
From: Vladislav Vlastovskiy
Date: Mon, 14 Apr 2014 13:21:24 +0400
Subject: Fixed convert bytes to str
Use compact function for convert---
 rest_framework/parsers.py | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py
index d49b17a4..4990971b 100644
--- a/rest_framework/parsers.py
+++ b/rest_framework/parsers.py
@@ -10,8 +10,7 @@ from django.core.files.uploadhandler import StopFutureHandlers
 from django.http import QueryDict
 from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser
 from django.http.multipartparser import MultiPartParserError, parse_header, ChunkIter
-from django.utils.encoding import force_str
-from rest_framework.compat import etree, six, yaml
+from rest_framework.compat import etree, six, yaml, force_text
 from rest_framework.exceptions import ParseError
 from rest_framework import renderers
 import json
@@ -290,6 +289,6 @@ class FileUploadParser(BaseParser):
         try:
             meta = parser_context['request'].META
             disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode('utf-8'))
-            return force_str(disposition[1]['filename'])
+            return force_text(disposition[1]['filename'])
         except (AttributeError, KeyError):
             pass
-- 
cgit v1.2.3
From 617c9825913cfc0cdeaa4405df0b885db0a9ff60 Mon Sep 17 00:00:00 2001
From: Miro Hrončok
Date: Tue, 15 Apr 2014 14:12:09 +0200
Subject: Add test for UnicodeYAMLRenderer
---
 rest_framework/tests/test_renderers.py | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/rest_framework/tests/test_renderers.py b/rest_framework/tests/test_renderers.py
index c7bf772e..7cb7d0f9 100644
--- a/rest_framework/tests/test_renderers.py
+++ b/rest_framework/tests/test_renderers.py
@@ -12,7 +12,7 @@ from rest_framework.compat import yaml, etree, patterns, url, include, six, Stri
 from rest_framework.response import Response
 from rest_framework.views import APIView
 from rest_framework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \
-    XMLRenderer, JSONPRenderer, BrowsableAPIRenderer, UnicodeJSONRenderer
+    XMLRenderer, JSONPRenderer, BrowsableAPIRenderer, UnicodeJSONRenderer, UnicodeYAMLRenderer
 from rest_framework.parsers import YAMLParser, XMLParser
 from rest_framework.settings import api_settings
 from rest_framework.test import APIRequestFactory
@@ -467,6 +467,17 @@ if yaml:
             self.assertTrue(string in content, '%r not in %r' % (string, content))
 
 
+    class UnicodeYAMLRendererTests(TestCase):
+        """
+        Tests specific for the Unicode YAML Renderer
+        """
+        def test_proper_encoding(self):
+            obj = {'countries': ['United Kingdom', 'France', 'España']}
+            renderer = UnicodeYAMLRenderer()
+            content = renderer.render(obj, 'application/yaml')
+            self.assertEqual(content.strip(), 'countries: [United Kingdom, France, España]'.encode('utf-8'))
+
+
 class XMLRendererTestCase(TestCase):
     """
     Tests specific to the XML Renderer
-- 
cgit v1.2.3
From ef1d65282771c806f68d717d57172597184db26c Mon Sep 17 00:00:00 2001
From: Miro Hrončok
Date: Tue, 15 Apr 2014 14:02:11 +0200
Subject: Introduce tests for urlize_quoted_links() function
---
 rest_framework/tests/test_urlizer.py | 38 ++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
 create mode 100644 rest_framework/tests/test_urlizer.py
diff --git a/rest_framework/tests/test_urlizer.py b/rest_framework/tests/test_urlizer.py
new file mode 100644
index 00000000..3dc8e8fe
--- /dev/null
+++ b/rest_framework/tests/test_urlizer.py
@@ -0,0 +1,38 @@
+from __future__ import unicode_literals
+from django.test import TestCase
+from rest_framework.templatetags.rest_framework import urlize_quoted_links
+import sys
+
+
+class URLizerTests(TestCase):
+    """
+    Test if both JSON and YAML URLs are transformed into links well
+    """
+    def _urlize_dict_check(self, data):
+        """
+        For all items in dict test assert that the value is urlized key
+        """
+        for original, urlized in data.items():
+            assert urlize_quoted_links(original, nofollow=False) == urlized
+
+    def test_json_with_url(self):
+        """
+        Test if JSON URLs are transformed into links well
+        """
+        data = {}
+        data['"url": "http://api/users/1/", '] = \
+            '"url": "http://api/users/1/", '
+        data['"foo_set": [\n    "http://api/foos/1/"\n], '] = \
+            '"foo_set": [\n    "http://api/foos/1/"\n], '
+        self._urlize_dict_check(data)
+
+    def test_yaml_with_url(self):
+        """
+        Test if YAML URLs are transformed into links well
+        """
+        data = {}
+        data['''{users: 'http://api/users/'}'''] = \
+            '''{users: 'http://api/users/'}'''
+        data['''foo_set: ['http://api/foos/1/']'''] = \
+            '''foo_set: ['http://api/foos/1/']'''
+        self._urlize_dict_check(data)
-- 
cgit v1.2.3
From a6e525cf3a22a01a4f9924e975ea7288d80ac5ef Mon Sep 17 00:00:00 2001
From: Sergey Sinitsyn
Date: Thu, 24 Apr 2014 15:58:53 +0600
Subject: Add help_text and verbose_name attribute mapping for related field
---
 rest_framework/serializers.py           |  8 ++++++++
 rest_framework/tests/models.py          |  3 ++-
 rest_framework/tests/test_serializer.py | 26 +++++++++++++++++++++++++-
 3 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index ea9509bf..9cb548a5 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -828,6 +828,10 @@ class ModelSerializer(Serializer):
 
         if model_field:
             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)
 
@@ -1088,6 +1092,10 @@ class HyperlinkedModelSerializer(ModelSerializer):
 
         if model_field:
             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:
             kwargs['lookup_field'] = self.opts.lookup_field
diff --git a/rest_framework/tests/models.py b/rest_framework/tests/models.py
index 6c8f2342..0256697a 100644
--- a/rest_framework/tests/models.py
+++ b/rest_framework/tests/models.py
@@ -143,7 +143,8 @@ class ForeignKeyTarget(RESTFrameworkModel):
 
 class ForeignKeySource(RESTFrameworkModel):
     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
diff --git a/rest_framework/tests/test_serializer.py b/rest_framework/tests/test_serializer.py
index 3ee2b38a..e688c823 100644
--- a/rest_framework/tests/test_serializer.py
+++ b/rest_framework/tests/test_serializer.py
@@ -9,7 +9,8 @@ from django.utils.translation import ugettext_lazy as _
 from rest_framework import serializers, fields, relations
 from rest_framework.tests.models import (HasPositiveIntegerAsChoice, Album, ActionItem, Anchor, BasicModel,
     BlankFieldModel, BlogPost, BlogPostComment, Book, CallableDefaultValueModel, DefaultValueModel,
-    ManyToManyModel, Person, ReadOnlyManyToManyModel, Photo, RESTFrameworkModel)
+    ManyToManyModel, Person, ReadOnlyManyToManyModel, Photo, RESTFrameworkModel,
+    ForeignKeySource, ManyToManySource)
 from rest_framework.tests.models import BasicModelSerializer
 import datetime
 import pickle
@@ -176,6 +177,16 @@ class PositiveIntegerAsChoiceSerializer(serializers.ModelSerializer):
         fields = ['some_integer']
 
 
+class ForeignKeySourceSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = ForeignKeySource
+
+
+class HyperlinkedForeignKeySourceSerializer(serializers.HyperlinkedModelSerializer):
+    class Meta:
+        model = ForeignKeySource
+
+
 class BasicTests(TestCase):
     def setUp(self):
         self.comment = Comment(
@@ -1600,6 +1611,19 @@ class ManyFieldHelpTextTest(TestCase):
         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')
 class AttributeMappingOnAutogeneratedFieldsTests(TestCase):
 
-- 
cgit v1.2.3
From f4a82dd5dadf95908c96c402f7f68b8e74c7de7a Mon Sep 17 00:00:00 2001
From: Xavier Ordoquy
Date: Thu, 24 Apr 2014 14:33:36 +0200
Subject: Updated the release notes.
---
 docs/topics/release-notes.md | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md
index 2bc8b2d6..335497ee 100644
--- a/docs/topics/release-notes.md
+++ b/docs/topics/release-notes.md
@@ -40,6 +40,25 @@ 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.13
 
 **Date**: 6th March 2014
-- 
cgit v1.2.3
From 82094554e5d267bcb550d3f7be26552befd7a1fe Mon Sep 17 00:00:00 2001
From: Kamil Niski
Date: Sun, 27 Apr 2014 02:54:47 +0200
Subject: Minor typo
---
 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 946a5954..8cdc5551 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -289,7 +289,7 @@ class WritableField(Field):
         self.validators = self.default_validators + validators
         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
         if isinstance(widget, type):
             widget = widget()
-- 
cgit v1.2.3
From 4a1ef6d4b15c504881662a2667564394cb333b6b Mon Sep 17 00:00:00 2001
From: Xavier Ordoquy
Date: Sun, 27 Apr 2014 11:52:33 +0200
Subject: Updated Django's versions.
---
 .travis.yml | 16 ++++++++--------
 tox.ini     | 26 +++++++++++++-------------
 2 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 60b48cba..bd6d2539 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,10 +7,10 @@ python:
   - "3.3"
 
 env:
-  - DJANGO="https://www.djangoproject.com/download/1.7b1/tarball/"
-  - DJANGO="django==1.6.2"
-  - DJANGO="django==1.5.5"
-  - DJANGO="django==1.4.10"
+  - 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="django==1.3.7"
 
 install:
@@ -23,7 +23,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.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=.
 
 script:
@@ -32,13 +32,13 @@ script:
 matrix:
   exclude:
     - 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"
-      env: DJANGO="django==1.4.10"
+      env: DJANGO="django==1.4.11"
     - python: "3.2"
       env: DJANGO="django==1.3.7"
     - python: "3.3"
-      env: DJANGO="django==1.4.10"
+      env: DJANGO="django==1.4.11"
     - python: "3.3"
       env: DJANGO="django==1.3.7"
 
diff --git a/tox.ini b/tox.ini
index 855ab0ce..e2121005 100644
--- a/tox.ini
+++ b/tox.ini
@@ -7,21 +7,21 @@ commands = {envpython} rest_framework/runtests/runtests.py
 
 [testenv:py3.3-django1.7]
 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
        defusedxml==0.3
        Pillow==2.3.0
 
 [testenv:py3.2-django1.7]
 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
        defusedxml==0.3
        Pillow==2.3.0
 
 [testenv:py2.7-django1.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
        defusedxml==0.3
        django-oauth-plus==2.2.1
@@ -32,21 +32,21 @@ deps = https://www.djangoproject.com/download/1.7b1/tarball/
 
 [testenv:py3.3-django1.6]
 basepython = python3.3
-deps = Django==1.6
+deps = Django==1.6.3
        django-filter==0.7
        defusedxml==0.3
        Pillow==2.3.0
 
 [testenv:py3.2-django1.6]
 basepython = python3.2
-deps = Django==1.6
+deps = Django==1.6.3
        django-filter==0.7
        defusedxml==0.3
        Pillow==2.3.0
 
 [testenv:py2.7-django1.6]
 basepython = python2.7
-deps = Django==1.6
+deps = Django==1.6.3
        django-filter==0.7
        defusedxml==0.3
        django-oauth-plus==2.2.1
@@ -57,7 +57,7 @@ deps = Django==1.6
 
 [testenv:py2.6-django1.6]
 basepython = python2.6
-deps = Django==1.6
+deps = Django==1.6.3
        django-filter==0.7
        defusedxml==0.3
        django-oauth-plus==2.2.1
@@ -68,21 +68,21 @@ deps = Django==1.6
 
 [testenv:py3.3-django1.5]
 basepython = python3.3
-deps = django==1.5.5
+deps = django==1.5.6
        django-filter==0.7
        defusedxml==0.3
        Pillow==2.3.0
 
 [testenv:py3.2-django1.5]
 basepython = python3.2
-deps = django==1.5.5
+deps = django==1.5.6
        django-filter==0.7
        defusedxml==0.3
        Pillow==2.3.0
 
 [testenv:py2.7-django1.5]
 basepython = python2.7
-deps = django==1.5.5
+deps = django==1.5.6
        django-filter==0.7
        defusedxml==0.3
        django-oauth-plus==2.2.1
@@ -93,7 +93,7 @@ deps = django==1.5.5
 
 [testenv:py2.6-django1.5]
 basepython = python2.6
-deps = django==1.5.5
+deps = django==1.5.6
        django-filter==0.7
        defusedxml==0.3
        django-oauth-plus==2.2.1
@@ -104,7 +104,7 @@ deps = django==1.5.5
 
 [testenv:py2.7-django1.4]
 basepython = python2.7
-deps = django==1.4.10
+deps = django==1.4.11
        django-filter==0.7
        defusedxml==0.3
        django-oauth-plus==2.2.1
@@ -115,7 +115,7 @@ deps = django==1.4.10
 
 [testenv:py2.6-django1.4]
 basepython = python2.6
-deps = django==1.4.10
+deps = django==1.4.11
        django-filter==0.7
        defusedxml==0.3
        django-oauth-plus==2.2.1
-- 
cgit v1.2.3
From 1c777ffe8b67c342bc1b27fefe67d1094a2f6b07 Mon Sep 17 00:00:00 2001
From: Max Peterson
Date: Mon, 28 Apr 2014 12:35:55 +0100
Subject: Ensure Token.generate_key returns a string.
---
 rest_framework/authtoken/models.py          | 2 +-
 rest_framework/tests/test_authentication.py | 6 ++++++
 2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/rest_framework/authtoken/models.py b/rest_framework/authtoken/models.py
index 8eac2cc4..167fa531 100644
--- a/rest_framework/authtoken/models.py
+++ b/rest_framework/authtoken/models.py
@@ -34,7 +34,7 @@ class Token(models.Model):
         return super(Token, self).save(*args, **kwargs)
 
     def generate_key(self):
-        return binascii.hexlify(os.urandom(20))
+        return binascii.hexlify(os.urandom(20)).decode()
 
     def __unicode__(self):
         return self.key
diff --git a/rest_framework/tests/test_authentication.py b/rest_framework/tests/test_authentication.py
index c37d2a51..8773f580 100644
--- a/rest_framework/tests/test_authentication.py
+++ b/rest_framework/tests/test_authentication.py
@@ -195,6 +195,12 @@ class TokenAuthTests(TestCase):
         token = Token.objects.create(user=self.user)
         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, str))
+
     def test_token_login_json(self):
         """Ensure token login view using JSON POST works."""
         client = APIClient(enforce_csrf_checks=True)
-- 
cgit v1.2.3
From 170fa10ae0f2b531a8011be33cc9417b9f71e698 Mon Sep 17 00:00:00 2001
From: Max Peterson
Date: Mon, 28 Apr 2014 13:10:34 +0100
Subject: Python < 3 compatibility.
---
 rest_framework/tests/test_authentication.py | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/rest_framework/tests/test_authentication.py b/rest_framework/tests/test_authentication.py
index 8773f580..34ce1b7a 100644
--- a/rest_framework/tests/test_authentication.py
+++ b/rest_framework/tests/test_authentication.py
@@ -199,7 +199,13 @@ class TokenAuthTests(TestCase):
         """Ensure generate_key returns a string"""
         token = Token()
         key = token.generate_key()
-        self.assertTrue(isinstance(key, str))
+        try:
+            # added in Python < 3
+            base = unicode
+        except NameError:
+            # added in Python >= 3
+            base = str
+        self.assertTrue(isinstance(key, base))
 
     def test_token_login_json(self):
         """Ensure token login view using JSON POST works."""
-- 
cgit v1.2.3
From 73597a16a2a6a388a08af923a1da8aa71d2f2848 Mon Sep 17 00:00:00 2001
From: Max Peterson
Date: Mon, 28 Apr 2014 13:13:51 +0100
Subject: Better Python < 3 compatibility.
---
 rest_framework/tests/test_authentication.py | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/rest_framework/tests/test_authentication.py b/rest_framework/tests/test_authentication.py
index 34ce1b7a..a1c43d9c 100644
--- a/rest_framework/tests/test_authentication.py
+++ b/rest_framework/tests/test_authentication.py
@@ -19,7 +19,7 @@ from rest_framework.authentication import (
     OAuth2Authentication
 )
 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 oauth, oauth_provider
 from rest_framework.test import APIRequestFactory, APIClient
@@ -199,13 +199,7 @@ class TokenAuthTests(TestCase):
         """Ensure generate_key returns a string"""
         token = Token()
         key = token.generate_key()
-        try:
-            # added in Python < 3
-            base = unicode
-        except NameError:
-            # added in Python >= 3
-            base = str
-        self.assertTrue(isinstance(key, base))
+        self.assertTrue(isinstance(key, six.string_types))
 
     def test_token_login_json(self):
         """Ensure token login view using JSON POST works."""
-- 
cgit v1.2.3
From 5e8f05a8de410125d6df7a8e27f61e94176a8897 Mon Sep 17 00:00:00 2001
From: dpetzel
Date: Mon, 28 Apr 2014 13:51:50 -0400
Subject: very minor typo in code example
---
 docs/api-guide/permissions.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md
index 6a0f48f4..50f669a2 100644
--- a/docs/api-guide/permissions.md
+++ b/docs/api-guide/permissions.md
@@ -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.
 
     from rest_framework.permissions import IsAuthenticated
-	from rest_framework.responses import Response
+	from rest_framework.response import Response
 	from rest_framework.views import APIView
 
     class ExampleView(APIView):
-- 
cgit v1.2.3
From d8cb85ef8fb0a0804d9b2c09d909ad99f69301c8 Mon Sep 17 00:00:00 2001
From: Laurent Bristiel
Date: Mon, 28 Apr 2014 22:00:36 +0200
Subject: typo
---
 docs/api-guide/generic-views.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md
index fb927ea8..7d06f246 100755
--- a/docs/api-guide/generic-views.md
+++ b/docs/api-guide/generic-views.md
@@ -70,7 +70,7 @@ The following attributes control the basic view behavior.
 
 **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**:
 
-- 
cgit v1.2.3