Opened 3 years ago

Last modified 3 years ago

#25306 new New feature

Allow a limit_choices_to callable to accept the current model instance

Reported by: Miles Hutson Owned by: nobody
Component: Forms Version: master
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


Real life problem: I have a site with two models Person and Storyteller. Storytellers can tell stories (represented by a TextField) about a Person. This relationship is represented by a ForeignKey from Storyteller to Person. An admin can choose a feature story about a Person. This is represented by a foreign key from Person to Storyteller. The change view for a given Person should only display Storytellers that have a foreign key to that person. There isn't an easy way to do this currently.

Change History (4)

comment:1 Changed 3 years ago by Tim Graham

How about using AdminSite.form and customizing the foreign key's queryset in form.__init__() as described in Changing the queryset.

comment:2 Changed 3 years ago by Miles Hutson

Thanks for the pointer Tim! I've been digging into the admin code, because I honestly hadn't needed to before. I'm sure that what you mentioned will end up being what I have to do. But part of the point of Django admin seems to be that people don't end up having to dive into the code for an admin interface when they're building a website. Hence why there are options like blank and limit_choices_to on fields. The suggestion in this feature request is that model fields have something similar to limit_choices_to. Perhaps this parameter could be passed a function. When a ForeignKey is populated in a change view, this function could be passed the relevant instance, and could return a queryset to filter on.

Perhaps this could be done by adding the behavior described to limit_choices_to? I believe this would involve modifying the __init__ method of forms.models.BaseModelForm.

Last edited 3 years ago by Tim Graham (previous) (diff)

comment:3 Changed 3 years ago by Miles Hutson

Anyone who googles this and finds themselves in the same rut, here's a useful stopgap. Override the ModelAdmin's form with a form subclassed off of this.

class LimitChoicesBaseForm(ModelForm):
    limit_choices_methods = []

    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
                 initial=None, error_class=ErrorList, label_suffix=None,
                 empty_permitted=False, instance=None):
        super(LimitChoicesBaseForm, self).__init__(data=data, files=files, auto_id=auto_id, prefix=prefix,
                                                   initial=initial, error_class=error_class, label_suffix=label_suffix,
                                                   empty_permitted=empty_permitted, instance=instance)
        for method in self.limit_choices_methods:
            formfield = self.fields[method['field_name']]
            if hasattr(formfield, 'queryset'):
                filter_q_object = method['method'](instance)
                formfield.queryset = formfield.queryset.filter(filter_q_object)

comment:4 Changed 3 years ago by Tim Graham

Component: contrib.adminForms
Summary: Should be an easier way to restrict the choices for a ForeignKeyAllow a limit_choices_to callable to accept the current model instance
Triage Stage: UnreviewedAccepted
Version: 1.8master

It might be possible to allow passing the instance to limit_choices_to, but I'm not sure. Accepting on the basis of investigating the idea. See #2445 for related work.

Note: See TracTickets for help on using tickets.
Back to Top