aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework/relations.py
diff options
context:
space:
mode:
Diffstat (limited to 'rest_framework/relations.py')
-rw-r--r--rest_framework/relations.py35
1 files changed, 24 insertions, 11 deletions
diff --git a/rest_framework/relations.py b/rest_framework/relations.py
index 7b119291..0b7c9d86 100644
--- a/rest_framework/relations.py
+++ b/rest_framework/relations.py
@@ -1,12 +1,13 @@
# coding: utf-8
from __future__ import unicode_literals
from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
-from django.core.urlresolvers import resolve, get_script_prefix, NoReverseMatch, Resolver404
+from django.core.urlresolvers import get_script_prefix, resolve, NoReverseMatch, Resolver404
from django.db.models.query import QuerySet
from django.utils import six
from django.utils.encoding import smart_text
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.translation import ugettext_lazy as _
+from rest_framework.compat import OrderedDict
from rest_framework.fields import get_attribute, empty, Field
from rest_framework.reverse import reverse
from rest_framework.utils import html
@@ -103,7 +104,7 @@ class RelatedField(Field):
@property
def choices(self):
- return dict([
+ return OrderedDict([
(
six.text_type(self.to_representation(item)),
six.text_type(item)
@@ -129,7 +130,7 @@ class StringRelatedField(RelatedField):
class PrimaryKeyRelatedField(RelatedField):
default_error_messages = {
'required': _('This field is required.'),
- 'does_not_exist': _("Invalid pk '{pk_value}' - object does not exist."),
+ 'does_not_exist': _('Invalid pk "{pk_value}" - object does not exist.'),
'incorrect_type': _('Incorrect type. Expected pk value, received {data_type}.'),
}
@@ -153,7 +154,7 @@ class HyperlinkedRelatedField(RelatedField):
default_error_messages = {
'required': _('This field is required.'),
- 'no_match': _('Invalid hyperlink - No URL match'),
+ 'no_match': _('Invalid hyperlink - No URL match.'),
'incorrect_match': _('Invalid hyperlink - Incorrect URL match.'),
'does_not_exist': _('Invalid hyperlink - Object does not exist.'),
'incorrect_type': _('Incorrect type. Expected URL string, received {data_type}.'),
@@ -166,11 +167,10 @@ class HyperlinkedRelatedField(RelatedField):
self.lookup_url_kwarg = kwargs.pop('lookup_url_kwarg', self.lookup_field)
self.format = kwargs.pop('format', None)
- # We include these simply for dependency injection in tests.
- # We can't add them as class attributes or they would expect an
+ # We include this simply for dependency injection in tests.
+ # We can't add it as a class attributes or it would expect an
# implicit `self` argument to be passed.
self.reverse = reverse
- self.resolve = resolve
super(HyperlinkedRelatedField, self).__init__(**kwargs)
@@ -204,6 +204,7 @@ class HyperlinkedRelatedField(RelatedField):
return self.reverse(view_name, kwargs=kwargs, request=request, format=format)
def to_internal_value(self, data):
+ request = self.context.get('request', None)
try:
http_prefix = data.startswith(('http:', 'https:'))
except AttributeError:
@@ -217,11 +218,18 @@ class HyperlinkedRelatedField(RelatedField):
data = '/' + data[len(prefix):]
try:
- match = self.resolve(data)
+ match = resolve(data)
except Resolver404:
self.fail('no_match')
- if match.view_name != self.view_name:
+ try:
+ expected_viewname = request.versioning_scheme.get_versioned_viewname(
+ self.view_name, request
+ )
+ except AttributeError:
+ expected_viewname = self.view_name
+
+ if match.view_name != expected_viewname:
self.fail('incorrect_match')
try:
@@ -291,7 +299,7 @@ class SlugRelatedField(RelatedField):
"""
default_error_messages = {
- 'does_not_exist': _("Object with {slug_name}={value} does not exist."),
+ 'does_not_exist': _('Object with {slug_name}={value} does not exist.'),
'invalid': _('Invalid value.'),
}
@@ -337,7 +345,12 @@ class ManyRelatedField(Field):
# We override the default field access in order to support
# lists in HTML forms.
if html.is_html_input(dictionary):
+ # Don't return [] if the update is partial
+ if self.field_name not in dictionary:
+ if getattr(self.root, 'partial', False):
+ return empty
return dictionary.getlist(self.field_name)
+
return dictionary.get(self.field_name, empty)
def to_internal_value(self, data):
@@ -364,7 +377,7 @@ class ManyRelatedField(Field):
(item, self.child_relation.to_representation(item))
for item in iterable
]
- return dict([
+ return OrderedDict([
(
six.text_type(item_representation),
six.text_type(item) + ' - ' + six.text_type(item_representation)