mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-08 06:14:47 +03:00
Merge e670284f07
into 53a356a380
This commit is contained in:
commit
8944a0762f
|
@ -1135,6 +1135,11 @@ class ModelSerializer(Serializer):
|
||||||
field_kwargs = get_relation_kwargs(field_name, relation_info)
|
field_kwargs = get_relation_kwargs(field_name, relation_info)
|
||||||
|
|
||||||
to_field = field_kwargs.pop('to_field', None)
|
to_field = field_kwargs.pop('to_field', None)
|
||||||
|
if relation_info.reverse:
|
||||||
|
if to_field and not relation_info.related_model_field.related_fields[0][1].primary_key:
|
||||||
|
field_kwargs['slug_field'] = to_field
|
||||||
|
field_class = self.serializer_related_to_field
|
||||||
|
else:
|
||||||
if to_field and not relation_info.related_model._meta.get_field(to_field).primary_key:
|
if to_field and not relation_info.related_model._meta.get_field(to_field).primary_key:
|
||||||
field_kwargs['slug_field'] = to_field
|
field_kwargs['slug_field'] = to_field
|
||||||
field_class = self.serializer_related_to_field
|
field_class = self.serializer_related_to_field
|
||||||
|
|
|
@ -238,7 +238,7 @@ def get_relation_kwargs(field_name, relation_info):
|
||||||
"""
|
"""
|
||||||
Creates a default instance of a flat relational field.
|
Creates a default instance of a flat relational field.
|
||||||
"""
|
"""
|
||||||
model_field, related_model, to_many, to_field, has_through_model = relation_info
|
model_field, related_model, related_model_field, to_many, to_field, has_through_model, reverse = relation_info
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'queryset': related_model._default_manager,
|
'queryset': related_model._default_manager,
|
||||||
'view_name': get_detail_view_name(related_model)
|
'view_name': get_detail_view_name(related_model)
|
||||||
|
|
|
@ -25,9 +25,11 @@ FieldInfo = namedtuple('FieldResult', [
|
||||||
RelationInfo = namedtuple('RelationInfo', [
|
RelationInfo = namedtuple('RelationInfo', [
|
||||||
'model_field',
|
'model_field',
|
||||||
'related_model',
|
'related_model',
|
||||||
|
'related_model_field',
|
||||||
'to_many',
|
'to_many',
|
||||||
'to_field',
|
'to_field',
|
||||||
'has_through_model'
|
'has_through_model',
|
||||||
|
'reverse'
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
@ -104,9 +106,11 @@ def _get_forward_relationships(opts):
|
||||||
forward_relations[field.name] = RelationInfo(
|
forward_relations[field.name] = RelationInfo(
|
||||||
model_field=field,
|
model_field=field,
|
||||||
related_model=_resolve_model(field.rel.to),
|
related_model=_resolve_model(field.rel.to),
|
||||||
|
related_model_field=None,
|
||||||
to_many=False,
|
to_many=False,
|
||||||
to_field=_get_to_field(field),
|
to_field=_get_to_field(field),
|
||||||
has_through_model=False
|
has_through_model=False,
|
||||||
|
reverse=False
|
||||||
)
|
)
|
||||||
|
|
||||||
# Deal with forward many-to-many relationships.
|
# Deal with forward many-to-many relationships.
|
||||||
|
@ -114,12 +118,14 @@ def _get_forward_relationships(opts):
|
||||||
forward_relations[field.name] = RelationInfo(
|
forward_relations[field.name] = RelationInfo(
|
||||||
model_field=field,
|
model_field=field,
|
||||||
related_model=_resolve_model(field.rel.to),
|
related_model=_resolve_model(field.rel.to),
|
||||||
|
related_model_field=None,
|
||||||
to_many=True,
|
to_many=True,
|
||||||
# manytomany do not have to_fields
|
# manytomany do not have to_fields
|
||||||
to_field=None,
|
to_field=None,
|
||||||
has_through_model=(
|
has_through_model=(
|
||||||
not field.rel.through._meta.auto_created
|
not field.rel.through._meta.auto_created
|
||||||
)
|
),
|
||||||
|
reverse=False
|
||||||
)
|
)
|
||||||
|
|
||||||
return forward_relations
|
return forward_relations
|
||||||
|
@ -141,9 +147,11 @@ def _get_reverse_relationships(opts):
|
||||||
reverse_relations[accessor_name] = RelationInfo(
|
reverse_relations[accessor_name] = RelationInfo(
|
||||||
model_field=None,
|
model_field=None,
|
||||||
related_model=related,
|
related_model=related,
|
||||||
|
related_model_field=relation.field,
|
||||||
to_many=relation.field.rel.multiple,
|
to_many=relation.field.rel.multiple,
|
||||||
to_field=_get_to_field(relation.field),
|
to_field=_get_to_field(relation.field),
|
||||||
has_through_model=False
|
has_through_model=False,
|
||||||
|
reverse=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Deal with reverse many-to-many relationships.
|
# Deal with reverse many-to-many relationships.
|
||||||
|
@ -154,13 +162,15 @@ def _get_reverse_relationships(opts):
|
||||||
reverse_relations[accessor_name] = RelationInfo(
|
reverse_relations[accessor_name] = RelationInfo(
|
||||||
model_field=None,
|
model_field=None,
|
||||||
related_model=related,
|
related_model=related,
|
||||||
|
related_model_field=relation.field,
|
||||||
to_many=True,
|
to_many=True,
|
||||||
# manytomany do not have to_fields
|
# manytomany do not have to_fields
|
||||||
to_field=None,
|
to_field=None,
|
||||||
has_through_model=(
|
has_through_model=(
|
||||||
(getattr(relation.field.rel, 'through', None) is not None) and
|
(getattr(relation.field.rel, 'through', None) is not None) and
|
||||||
not relation.field.rel.through._meta.auto_created
|
not relation.field.rel.through._meta.auto_created
|
||||||
)
|
),
|
||||||
|
reverse=True
|
||||||
)
|
)
|
||||||
|
|
||||||
return reverse_relations
|
return reverse_relations
|
||||||
|
|
|
@ -89,6 +89,15 @@ class ChoicesModel(models.Model):
|
||||||
choices_field_with_nonstandard_args = models.DecimalField(max_digits=3, decimal_places=1, choices=DECIMAL_CHOICES, verbose_name='A label')
|
choices_field_with_nonstandard_args = models.DecimalField(max_digits=3, decimal_places=1, choices=DECIMAL_CHOICES, verbose_name='A label')
|
||||||
|
|
||||||
|
|
||||||
|
class Issue3674ParentModel(models.Model):
|
||||||
|
title = models.CharField(max_length=64)
|
||||||
|
|
||||||
|
|
||||||
|
class Issue3674ChildModel(models.Model):
|
||||||
|
parent = models.ForeignKey(Issue3674ParentModel, related_name='children')
|
||||||
|
value = models.CharField(primary_key=True, max_length=64)
|
||||||
|
|
||||||
|
|
||||||
class TestModelSerializer(TestCase):
|
class TestModelSerializer(TestCase):
|
||||||
def test_create_method(self):
|
def test_create_method(self):
|
||||||
class TestSerializer(serializers.ModelSerializer):
|
class TestSerializer(serializers.ModelSerializer):
|
||||||
|
@ -936,3 +945,62 @@ class TestMetaInheritance(TestCase):
|
||||||
self.assertEqual(unicode_repr(ChildSerializer()), child_expected)
|
self.assertEqual(unicode_repr(ChildSerializer()), child_expected)
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), test_expected)
|
self.assertEqual(unicode_repr(TestSerializer()), test_expected)
|
||||||
self.assertEqual(unicode_repr(ChildSerializer()), child_expected)
|
self.assertEqual(unicode_repr(ChildSerializer()), child_expected)
|
||||||
|
|
||||||
|
|
||||||
|
class Issue3674Test(TestCase):
|
||||||
|
def test_nonPK_foreignkey_model_serializer(self):
|
||||||
|
class TestParentModel(models.Model):
|
||||||
|
title = models.CharField(max_length=64)
|
||||||
|
|
||||||
|
class TestChildModel(models.Model):
|
||||||
|
parent = models.ForeignKey(TestParentModel, related_name='children')
|
||||||
|
value = models.CharField(primary_key=True, max_length=64)
|
||||||
|
|
||||||
|
class TestChildModelSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = TestChildModel
|
||||||
|
fields = ('value', 'parent')
|
||||||
|
|
||||||
|
class TestParentModelSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = TestParentModel
|
||||||
|
fields = ('id', 'title', 'children')
|
||||||
|
|
||||||
|
parent_expected = dedent("""
|
||||||
|
TestParentModelSerializer():
|
||||||
|
id = IntegerField(label='ID', read_only=True)
|
||||||
|
title = CharField(max_length=64)
|
||||||
|
children = PrimaryKeyRelatedField(many=True, queryset=TestChildModel.objects.all())
|
||||||
|
""")
|
||||||
|
self.assertEqual(unicode_repr(TestParentModelSerializer()), parent_expected)
|
||||||
|
|
||||||
|
child_expected = dedent("""
|
||||||
|
TestChildModelSerializer():
|
||||||
|
value = CharField(max_length=64, validators=[<UniqueValidator(queryset=TestChildModel.objects.all())>])
|
||||||
|
parent = PrimaryKeyRelatedField(queryset=TestParentModel.objects.all())
|
||||||
|
""")
|
||||||
|
self.assertEqual(unicode_repr(TestChildModelSerializer()), child_expected)
|
||||||
|
|
||||||
|
def test_nonID_PK_foreignkey_model_serializer(self):
|
||||||
|
|
||||||
|
class TestChildModelSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Issue3674ChildModel
|
||||||
|
fields = ('value', 'parent')
|
||||||
|
|
||||||
|
class TestParentModelSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Issue3674ParentModel
|
||||||
|
fields = ('id', 'title', 'children')
|
||||||
|
|
||||||
|
parent = Issue3674ParentModel.objects.create(title='abc')
|
||||||
|
child = Issue3674ChildModel.objects.create(value='def', parent=parent)
|
||||||
|
|
||||||
|
parent_serializer = TestParentModelSerializer(parent)
|
||||||
|
child_serializer = TestChildModelSerializer(child)
|
||||||
|
|
||||||
|
parent_expected = {'children': ['def'], 'id': 1, 'title': 'abc'}
|
||||||
|
self.assertEqual(parent_serializer.data, parent_expected)
|
||||||
|
|
||||||
|
child_expected = {'parent': 1, 'value': 'def'}
|
||||||
|
self.assertEqual(child_serializer.data, child_expected)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user