Limiting foreign key options in a `ModelForm`, subject to another value

I want a ModelForm to have limited options for a foreign key, depending on another value on the form.

It looks like the following code, based roughly on this StackOverflow answer, would do what I want, except update the foreign key options when the value changes. At least, that’s what the comments on the answer suggest, and it would make sense, because it only seems to set the QuerySet when the form is instantiated:

from django import forms
from django.contrib import admin 
from models import *

class SupplierAdminForm(forms.ModelForm):
    class Meta:
        model = Supplier
        fields = "__all__"

    def __init__(self, *args, **kwargs):
        super(SupplierAdminForm, self).__init__(*args, **kwargs)
        if self.instance and self.instance.dog:
            self.fields['cat'].queryset = Cat.objects.filter(liked_dogs=self.instance.dog)
        else:
            self.fields['cat'].queryset = Cat.objects.none()

class SupplierAdmin(admin.ModelAdmin):
    form = SupplierAdminForm

I suppose the form would need to have some AJAX code to fetch a new queryset, each time the dog value changes in the above example. I’m not sure if that’s within the scope of Django’s forms.

In an ideal world, if I added a constraint to the model, the form would automatically include this. But again, I don’t know whether this functionality exists. And for my purposes, a base form that does what I want, that I could code different subclasses from (e.g. for the admin tool and for the user-facing site), would already be marvellous.

I basically just want to avoid writing unnecessary code to reinvent the wheel for what seems a reasonably common use case. So, does this kind of functionality exist, either in stock Django, or in a reputable third-party library?

I’m sorry if this is already answered in this forum. I’ve browsed the posts in this forum about Passing an altered queryset to ModelChoiceField and altering queryset in a field of generic CreateView form, but they honestly go a bit over my head and I’m not sure they do what I want. Maybe this post essentially boils down to the same thing, but it has no answers yet.

I’m not sure I’m following exactly what you’re trying to do here, so I’d like to step back and address this from the perspective of the fundamentals.

When a view is invoked, it returns a page. Once that page has been returned, the view has gone out of scope and is no longer available.

This means that once Django has rendered a form and returned it to the browser, Django is not involved at all with anything going on in the browser, until another view is invoked.

So in the case of a “live update” of a form on a page, yes, it requires JavaScript - possibly using AJAX to retrieve data to be used to update the page.

There are many ways (and libraries) available to do this. Personally, I’m a big fan of HTMX - it’s now my tool-of-choice for dynamic page updates in the absence of a full page reload. Previously, I’ve done this kind of work with jQuery. You can also do this with native JavaScript.

Regardless of the JavaScript being used, you will probably end up writing additional views to return the specific data being requests. For example, if it’s a select list that is being changed, you could create a view that returns the set of <option> elements to populate that widget.

Or, if this isn’t what you’re asking about, I’ll have to ask you for a more detailed explanation of exactly what it is you’re trying to achive, both in the description of the data involved and an outline of the sequence of events (including what you want to have happen, where, and when in that sequence), perhaps in the context of a real-life example.

Yep, that’s it, and I’ve been getting into HTMX as well, since kids these days look at jQuery like it’s Fortran.

I thought this would be a common enough use case for me to be able to just define some constraints in the model and have a generic view class define the required AJAX endpoints, and a form render the required front-end code to call them. But there are probably a lot of other steps to it that I haven’t yet thought of, that would still require a lot of custom code.

So then, regarding the admin tool, I assume the admin user will just need to learn which options are allowed, that won’t be rejected when the model object is cleaned? Or is there a pattern I can follow to limit the foreign key options the admin user will have to choose from?

Thanks.

Not a “lot”, but enough to probably make a generalized solution impractical. (The definition of the URLs to handle the specialized requests being one of them. Defining what the restrictions are would be another.)

This is not necessarily true.

You could add a custom JavaScript file to the desired admin form that when a form element is changed, it changes the available options in another field.