mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-01-24 00:04:16 +03:00
Merge pull request #694 from craigds/master
fix function names and dotted lookups for use in PrimaryKeyRelatedField
This commit is contained in:
commit
9fe6a103ec
|
@ -221,12 +221,20 @@ class PrimaryKeyRelatedField(RelatedField):
|
|||
def field_to_native(self, obj, field_name):
|
||||
if self.many:
|
||||
# To-many relationship
|
||||
try:
|
||||
|
||||
queryset = None
|
||||
if not self.source:
|
||||
# Prefer obj.serializable_value for performance reasons
|
||||
queryset = obj.serializable_value(self.source or field_name)
|
||||
except AttributeError:
|
||||
try:
|
||||
queryset = obj.serializable_value(field_name)
|
||||
except AttributeError:
|
||||
pass
|
||||
if queryset is None:
|
||||
# RelatedManager (reverse relationship)
|
||||
queryset = getattr(obj, self.source or field_name)
|
||||
source = self.source or field_name
|
||||
queryset = obj
|
||||
for component in source.split('.'):
|
||||
queryset = get_component(queryset, component)
|
||||
|
||||
# Forward relationship
|
||||
return [self.to_native(item.pk) for item in queryset.all()]
|
||||
|
|
|
@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
|||
from django.db import models
|
||||
from django.test import TestCase
|
||||
from rest_framework import serializers
|
||||
from rest_framework.tests.models import BlogPost
|
||||
|
||||
|
||||
class NullModel(models.Model):
|
||||
|
@ -33,7 +34,7 @@ class FieldTests(TestCase):
|
|||
self.assertRaises(serializers.ValidationError, field.from_native, [])
|
||||
|
||||
|
||||
class TestManyRelateMixin(TestCase):
|
||||
class TestManyRelatedMixin(TestCase):
|
||||
def test_missing_many_to_many_related_field(self):
|
||||
'''
|
||||
Regression test for #632
|
||||
|
@ -45,3 +46,55 @@ class TestManyRelateMixin(TestCase):
|
|||
into = {}
|
||||
field.field_from_native({}, None, 'field_name', into)
|
||||
self.assertEqual(into['field_name'], [])
|
||||
|
||||
|
||||
# Regression tests for #694 (`source` attribute on related fields)
|
||||
|
||||
class RelatedFieldSourceTests(TestCase):
|
||||
def test_related_manager_source(self):
|
||||
"""
|
||||
Relational fields should be able to use manager-returning methods as their source.
|
||||
"""
|
||||
BlogPost.objects.create(title='blah')
|
||||
field = serializers.RelatedField(many=True, source='get_blogposts_manager')
|
||||
|
||||
class ClassWithManagerMethod(object):
|
||||
def get_blogposts_manager(self):
|
||||
return BlogPost.objects
|
||||
|
||||
obj = ClassWithManagerMethod()
|
||||
value = field.field_to_native(obj, 'field_name')
|
||||
self.assertEqual(value, ['BlogPost object'])
|
||||
|
||||
def test_related_queryset_source(self):
|
||||
"""
|
||||
Relational fields should be able to use queryset-returning methods as their source.
|
||||
"""
|
||||
BlogPost.objects.create(title='blah')
|
||||
field = serializers.RelatedField(many=True, source='get_blogposts_queryset')
|
||||
|
||||
class ClassWithQuerysetMethod(object):
|
||||
def get_blogposts_queryset(self):
|
||||
return BlogPost.objects.all()
|
||||
|
||||
obj = ClassWithQuerysetMethod()
|
||||
value = field.field_to_native(obj, 'field_name')
|
||||
self.assertEqual(value, ['BlogPost object'])
|
||||
|
||||
def test_dotted_source(self):
|
||||
"""
|
||||
Source argument should support dotted.source notation.
|
||||
"""
|
||||
BlogPost.objects.create(title='blah')
|
||||
field = serializers.RelatedField(many=True, source='a.b.c')
|
||||
|
||||
class ClassWithQuerysetMethod(object):
|
||||
a = {
|
||||
'b': {
|
||||
'c': BlogPost.objects.all()
|
||||
}
|
||||
}
|
||||
|
||||
obj = ClassWithQuerysetMethod()
|
||||
value = field.field_to_native(obj, 'field_name')
|
||||
self.assertEqual(value, ['BlogPost object'])
|
||||
|
|
|
@ -4,6 +4,7 @@ from django.test.client import RequestFactory
|
|||
from rest_framework import serializers
|
||||
from rest_framework.compat import patterns, url
|
||||
from rest_framework.tests.models import (
|
||||
BlogPost,
|
||||
ManyToManyTarget, ManyToManySource, ForeignKeyTarget, ForeignKeySource,
|
||||
NullableForeignKeySource, OneToOneTarget, NullableOneToOneSource
|
||||
)
|
||||
|
@ -16,6 +17,7 @@ def dummy_view(request, pk):
|
|||
pass
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^dummyurl/(?P<pk>[0-9]+)/$', dummy_view, name='dummy-url'),
|
||||
url(r'^manytomanysource/(?P<pk>[0-9]+)/$', dummy_view, name='manytomanysource-detail'),
|
||||
url(r'^manytomanytarget/(?P<pk>[0-9]+)/$', dummy_view, name='manytomanytarget-detail'),
|
||||
url(r'^foreignkeysource/(?P<pk>[0-9]+)/$', dummy_view, name='foreignkeysource-detail'),
|
||||
|
@ -451,3 +453,72 @@ class HyperlinkedNullableOneToOneTests(TestCase):
|
|||
{'url': 'http://testserver/onetoonetarget/2/', 'name': 'target-2', 'nullable_source': None},
|
||||
]
|
||||
self.assertEqual(serializer.data, expected)
|
||||
|
||||
|
||||
# Regression tests for #694 (`source` attribute on related fields)
|
||||
|
||||
class HyperlinkedRelatedFieldSourceTests(TestCase):
|
||||
urls = 'rest_framework.tests.relations_hyperlink'
|
||||
|
||||
def test_related_manager_source(self):
|
||||
"""
|
||||
Relational fields should be able to use manager-returning methods as their source.
|
||||
"""
|
||||
BlogPost.objects.create(title='blah')
|
||||
field = serializers.HyperlinkedRelatedField(
|
||||
many=True,
|
||||
source='get_blogposts_manager',
|
||||
view_name='dummy-url',
|
||||
)
|
||||
field.context = {'request': request}
|
||||
|
||||
class ClassWithManagerMethod(object):
|
||||
def get_blogposts_manager(self):
|
||||
return BlogPost.objects
|
||||
|
||||
obj = ClassWithManagerMethod()
|
||||
value = field.field_to_native(obj, 'field_name')
|
||||
self.assertEqual(value, ['http://testserver/dummyurl/1/'])
|
||||
|
||||
def test_related_queryset_source(self):
|
||||
"""
|
||||
Relational fields should be able to use queryset-returning methods as their source.
|
||||
"""
|
||||
BlogPost.objects.create(title='blah')
|
||||
field = serializers.HyperlinkedRelatedField(
|
||||
many=True,
|
||||
source='get_blogposts_queryset',
|
||||
view_name='dummy-url',
|
||||
)
|
||||
field.context = {'request': request}
|
||||
|
||||
class ClassWithQuerysetMethod(object):
|
||||
def get_blogposts_queryset(self):
|
||||
return BlogPost.objects.all()
|
||||
|
||||
obj = ClassWithQuerysetMethod()
|
||||
value = field.field_to_native(obj, 'field_name')
|
||||
self.assertEqual(value, ['http://testserver/dummyurl/1/'])
|
||||
|
||||
def test_dotted_source(self):
|
||||
"""
|
||||
Source argument should support dotted.source notation.
|
||||
"""
|
||||
BlogPost.objects.create(title='blah')
|
||||
field = serializers.HyperlinkedRelatedField(
|
||||
many=True,
|
||||
source='a.b.c',
|
||||
view_name='dummy-url',
|
||||
)
|
||||
field.context = {'request': request}
|
||||
|
||||
class ClassWithQuerysetMethod(object):
|
||||
a = {
|
||||
'b': {
|
||||
'c': BlogPost.objects.all()
|
||||
}
|
||||
}
|
||||
|
||||
obj = ClassWithQuerysetMethod()
|
||||
value = field.field_to_native(obj, 'field_name')
|
||||
self.assertEqual(value, ['http://testserver/dummyurl/1/'])
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
from __future__ import unicode_literals
|
||||
from django.test import TestCase
|
||||
from rest_framework import serializers
|
||||
from rest_framework.tests.models import ManyToManyTarget, ManyToManySource, ForeignKeyTarget, ForeignKeySource, NullableForeignKeySource, OneToOneTarget, NullableOneToOneSource
|
||||
from rest_framework.tests.models import (
|
||||
BlogPost, ManyToManyTarget, ManyToManySource, ForeignKeyTarget, ForeignKeySource,
|
||||
NullableForeignKeySource, OneToOneTarget, NullableOneToOneSource,
|
||||
)
|
||||
from rest_framework.compat import six
|
||||
|
||||
|
||||
|
@ -421,3 +424,55 @@ class PKNullableOneToOneTests(TestCase):
|
|||
{'id': 2, 'name': 'target-2', 'nullable_source': 1},
|
||||
]
|
||||
self.assertEqual(serializer.data, expected)
|
||||
|
||||
|
||||
# Regression tests for #694 (`source` attribute on related fields)
|
||||
|
||||
class PrimaryKeyRelatedFieldSourceTests(TestCase):
|
||||
def test_related_manager_source(self):
|
||||
"""
|
||||
Relational fields should be able to use manager-returning methods as their source.
|
||||
"""
|
||||
BlogPost.objects.create(title='blah')
|
||||
field = serializers.PrimaryKeyRelatedField(many=True, source='get_blogposts_manager')
|
||||
|
||||
class ClassWithManagerMethod(object):
|
||||
def get_blogposts_manager(self):
|
||||
return BlogPost.objects
|
||||
|
||||
obj = ClassWithManagerMethod()
|
||||
value = field.field_to_native(obj, 'field_name')
|
||||
self.assertEqual(value, [1])
|
||||
|
||||
def test_related_queryset_source(self):
|
||||
"""
|
||||
Relational fields should be able to use queryset-returning methods as their source.
|
||||
"""
|
||||
BlogPost.objects.create(title='blah')
|
||||
field = serializers.PrimaryKeyRelatedField(many=True, source='get_blogposts_queryset')
|
||||
|
||||
class ClassWithQuerysetMethod(object):
|
||||
def get_blogposts_queryset(self):
|
||||
return BlogPost.objects.all()
|
||||
|
||||
obj = ClassWithQuerysetMethod()
|
||||
value = field.field_to_native(obj, 'field_name')
|
||||
self.assertEqual(value, [1])
|
||||
|
||||
def test_dotted_source(self):
|
||||
"""
|
||||
Source argument should support dotted.source notation.
|
||||
"""
|
||||
BlogPost.objects.create(title='blah')
|
||||
field = serializers.PrimaryKeyRelatedField(many=True, source='a.b.c')
|
||||
|
||||
class ClassWithQuerysetMethod(object):
|
||||
a = {
|
||||
'b': {
|
||||
'c': BlogPost.objects.all()
|
||||
}
|
||||
}
|
||||
|
||||
obj = ClassWithQuerysetMethod()
|
||||
value = field.field_to_native(obj, 'field_name')
|
||||
self.assertEqual(value, [1])
|
||||
|
|
|
@ -871,23 +871,6 @@ class RelatedTraversalTest(TestCase):
|
|||
|
||||
self.assertEqual(serializer.data, expected)
|
||||
|
||||
def test_queryset_nested_traversal(self):
|
||||
"""
|
||||
Relational fields should be able to use methods as their source.
|
||||
"""
|
||||
BlogPost.objects.create(title='blah')
|
||||
|
||||
class QuerysetMethodSerializer(serializers.Serializer):
|
||||
blogposts = serializers.RelatedField(many=True, source='get_all_blogposts')
|
||||
|
||||
class ClassWithQuerysetMethod(object):
|
||||
def get_all_blogposts(self):
|
||||
return BlogPost.objects
|
||||
|
||||
obj = ClassWithQuerysetMethod()
|
||||
serializer = QuerysetMethodSerializer(obj)
|
||||
self.assertEqual(serializer.data, {'blogposts': ['BlogPost object']})
|
||||
|
||||
|
||||
class SerializerMethodFieldTests(TestCase):
|
||||
def setUp(self):
|
||||
|
|
Loading…
Reference in New Issue
Block a user