Skip to content

Commit 3ebfcdb

Browse files
committed
Respect limit_choices_to for ForeignKey relations (fixes iommirocks#441)
1 parent 781c252 commit 3ebfcdb

File tree

6 files changed

+80
-7
lines changed

6 files changed

+80
-7
lines changed

iommi/_db_compat.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,16 @@ def setup_db_compat_iommi():
176176
register_edit_column_factory(SortOrderField, shortcut_name='reorder_handle')
177177

178178

179+
def choices_from_model_field(model, model_field):
180+
queryset = model.objects.all()
181+
limit_choices_to = model_field.remote_field.limit_choices_to
182+
if limit_choices_to:
183+
if callable(limit_choices_to):
184+
limit_choices_to = limit_choices_to()
185+
queryset = queryset.complex_filter(limit_choices_to)
186+
return queryset
187+
188+
179189
def base_defaults_factory(model_field):
180190
from iommi.base import capitalize
181191

iommi/form.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@
4646
from django.utils.translation import gettext_lazy
4747
from django.templatetags.static import static
4848

49-
from iommi._db_compat import field_defaults_factory
49+
from iommi._db_compat import (
50+
choices_from_model_field,
51+
field_defaults_factory,
52+
)
5053
from iommi._web_compat import (
5154
csrf,
5255
format_html,
@@ -1517,7 +1520,7 @@ def foreign_key(cls, model_field, model, **kwargs):
15171520
del model
15181521
setdefaults_path(
15191522
kwargs,
1520-
choices=model_field.foreign_related_fields[0].model.objects.all(),
1523+
choices=choices_from_model_field(model_field.foreign_related_fields[0].model, model_field),
15211524
)
15221525
return cls.choice_queryset(model_field=model_field, **kwargs)
15231526

@@ -1541,7 +1544,7 @@ def foreign_key_reverse(cls, *, model_field, **kwargs):
15411544
def many_to_many(cls, model_field, **kwargs):
15421545
setdefaults_path(
15431546
kwargs,
1544-
choices=model_field.remote_field.model.objects.all(),
1547+
choices=choices_from_model_field(model_field.remote_field.model, model_field),
15451548
read_from_instance=many_to_many_factory_read_from_instance,
15461549
write_to_instance=many_to_many_factory_write_to_instance,
15471550
extra__django_related_field=True,

iommi/form__tests.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,6 +1591,47 @@ class MyForm(Form):
15911591
assert set(choices) == set(Foo.objects.all())
15921592

15931593

1594+
@pytest.mark.django_db
1595+
def test_field_from_model_foreign_key_limit_choices_to():
1596+
from tests.models import Foo, LimitChoicesToFKTest
1597+
1598+
Foo.objects.create(foo=2)
1599+
b = Foo.objects.create(foo=3)
1600+
c = Foo.objects.create(foo=5)
1601+
1602+
class MyForm(Form):
1603+
foo_fk = Field.from_model(LimitChoicesToFKTest, 'foo_fk')
1604+
1605+
form = MyForm().bind(request=req('get'))
1606+
assert set(form.fields.foo_fk.choices) == {b, c}
1607+
1608+
1609+
@pytest.mark.django_db
1610+
def test_field_from_model_many_to_many_limit_choices_to():
1611+
from tests.models import Foo, LimitChoicesToM2MTest
1612+
1613+
Foo.objects.create(foo=2)
1614+
b = Foo.objects.create(foo=3)
1615+
c = Foo.objects.create(foo=5)
1616+
1617+
class MyForm(Form):
1618+
foo_m2m = Field.from_model(LimitChoicesToM2MTest, 'foo_m2m')
1619+
1620+
form = MyForm().bind(request=req('get'))
1621+
assert set(form.fields.foo_m2m.choices) == {b, c}
1622+
1623+
1624+
@pytest.mark.django_db
1625+
def test_field_from_model_foreign_key_to_proxy_model():
1626+
from tests.models import FKToFooProxyTest, Foo
1627+
1628+
class MyForm(Form):
1629+
foo_fk = Field.from_model(FKToFooProxyTest, 'foo_fk')
1630+
1631+
form = MyForm().bind(request=req('get'))
1632+
assert form.fields.foo_fk.choices.model is Foo
1633+
1634+
15941635
@pytest.mark.django_db
15951636
def test_field_from_model_many_to_many():
15961637
from django.db.models import QuerySet

iommi/query.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
ZeroOrMore
4040
)
4141

42+
from iommi._db_compat import choices_from_model_field
4243
from iommi._web_compat import (
4344
render_template,
4445
Template,
@@ -546,7 +547,7 @@ def duration(cls, **kwargs):
546547
def foreign_key(cls, model_field, **kwargs):
547548
setdefaults_path(
548549
kwargs,
549-
choices=model_field.foreign_related_fields[0].model.objects.all(),
550+
choices=choices_from_model_field(model_field.foreign_related_fields[0].model, model_field),
550551
)
551552
return cls.choice_queryset(model_field=model_field, **kwargs)
552553

@@ -569,7 +570,7 @@ def foreign_key_reverse(cls, *, model_field, **kwargs):
569570
def many_to_many(cls, model_field, **kwargs):
570571
setdefaults_path(
571572
kwargs,
572-
choices=model_field.remote_field.model.objects.all(),
573+
choices=choices_from_model_field(model_field.remote_field.model, model_field),
573574
extra__django_related_field=True,
574575
)
575576
return cls.multi_choice_queryset(model_field=model_field, **kwargs)

iommi/table.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
)
4545
from django.utils.translation import gettext_lazy
4646

47+
from iommi._db_compat import choices_from_model_field
4748
from iommi._web_compat import (
4849
HttpResponse,
4950
HttpResponseRedirect,
@@ -1008,7 +1009,7 @@ def duration(cls, **kwargs):
10081009
def many_to_many(cls, model_field, **kwargs):
10091010
setdefaults_path(
10101011
kwargs,
1011-
choices=model_field.remote_field.model.objects.all(),
1012+
choices=choices_from_model_field(model_field.remote_field.model, model_field),
10121013
model_field=model_field,
10131014
)
10141015
return cls.multi_choice_queryset(**kwargs)
@@ -1038,7 +1039,7 @@ def foreign_key(cls, model_field, **kwargs):
10381039
)
10391040
setdefaults_path(
10401041
kwargs,
1041-
choices=remote_model.objects.all(),
1042+
choices=choices_from_model_field(remote_model, model_field),
10421043
)
10431044
return cls.choice_queryset(model_field=model_field, **kwargs)
10441045

tests/models.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,3 +327,20 @@ class UuidPKModel(Model):
327327

328328
class AttachmentModel(Model):
329329
file = FileField(null=True)
330+
331+
332+
class LimitChoicesToFKTest(Model):
333+
foo_fk = ForeignKey(Foo, on_delete=CASCADE, limit_choices_to={'foo__gte': 3})
334+
335+
336+
class LimitChoicesToM2MTest(Model):
337+
foo_m2m = ManyToManyField(Foo, limit_choices_to={'foo__gte': 3})
338+
339+
340+
class FooProxy(Foo):
341+
class Meta:
342+
proxy = True
343+
344+
345+
class FKToFooProxyTest(Model):
346+
foo_fk = ForeignKey(FooProxy, on_delete=CASCADE)

0 commit comments

Comments
 (0)