From c5748c4d28ea67e9072e260659fb476897d04b22 Mon Sep 17 00:00:00 2001 From: Adrien Brunet Date: Tue, 8 Jan 2019 14:49:47 +0100 Subject: [PATCH] Fix #1811: take limit_choices_to into account with FK (#6371) * Fix issue1811: take limit_choices_to into account with FK * Issue 1811: Add tests to illustrate issue * Filter queryset only if limit_choices_to exists * Move test_relations_with_limited_querysets file within test_relations_pk * move limit_choices_to logic from relations.py to utils/field_mapping.py * move limit_choices_to above other check to avoid conflicts --- rest_framework/utils/field_mapping.py | 4 ++++ tests/models.py | 7 +++++++ tests/test_relations_pk.py | 26 ++++++++++++++++++++++---- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index d3044ff59d0..991f20f17a6 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -249,6 +249,10 @@ def get_relation_kwargs(field_name, relation_info): if to_field: kwargs['to_field'] = to_field + limit_choices_to = model_field and model_field.get_limit_choices_to() + if limit_choices_to: + kwargs['queryset'] = kwargs['queryset'].filter(**limit_choices_to) + if has_through_model: kwargs['read_only'] = True kwargs.pop('queryset', None) diff --git a/tests/models.py b/tests/models.py index 6aeceb93458..55f250e046b 100644 --- a/tests/models.py +++ b/tests/models.py @@ -52,6 +52,13 @@ class ForeignKeySource(RESTFrameworkModel): on_delete=models.CASCADE) +class ForeignKeySourceWithLimitedChoices(RESTFrameworkModel): + target = models.ForeignKey(ForeignKeyTarget, help_text='Target', + verbose_name='Target', + limit_choices_to={"name__startswith": "limited-"}, + on_delete=models.CASCADE) + + # Nullable ForeignKey class NullableForeignKeySource(RESTFrameworkModel): name = models.CharField(max_length=100) diff --git a/tests/test_relations_pk.py b/tests/test_relations_pk.py index 3317d6251e2..31b6bb8677f 100644 --- a/tests/test_relations_pk.py +++ b/tests/test_relations_pk.py @@ -5,10 +5,10 @@ from rest_framework import serializers from tests.models import ( - ForeignKeySource, ForeignKeyTarget, ManyToManySource, ManyToManyTarget, - NullableForeignKeySource, NullableOneToOneSource, - NullableUUIDForeignKeySource, OneToOnePKSource, OneToOneTarget, - UUIDForeignKeyTarget + ForeignKeySource, ForeignKeySourceWithLimitedChoices, ForeignKeyTarget, + ManyToManySource, ManyToManyTarget, NullableForeignKeySource, + NullableOneToOneSource, NullableUUIDForeignKeySource, OneToOnePKSource, + OneToOneTarget, UUIDForeignKeyTarget ) @@ -38,6 +38,12 @@ class Meta: fields = ('id', 'name', 'target') +class ForeignKeySourceWithLimitedChoicesSerializer(serializers.ModelSerializer): + class Meta: + model = ForeignKeySourceWithLimitedChoices + fields = ("id", "target") + + # Nullable ForeignKey class NullableForeignKeySourceSerializer(serializers.ModelSerializer): class Meta: @@ -360,6 +366,18 @@ class Meta(ForeignKeySourceSerializer.Meta): serializer.is_valid(raise_exception=True) assert 'target' not in serializer.validated_data + def test_queryset_size_without_limited_choices(self): + limited_target = ForeignKeyTarget(name="limited-target") + limited_target.save() + queryset = ForeignKeySourceSerializer().fields["target"].get_queryset() + assert len(queryset) == 3 + + def test_queryset_size_with_limited_choices(self): + limited_target = ForeignKeyTarget(name="limited-target") + limited_target.save() + queryset = ForeignKeySourceWithLimitedChoicesSerializer().fields["target"].get_queryset() + assert len(queryset) == 1 + class PKNullableForeignKeyTests(TestCase): def setUp(self):