django Admin – Filter foreign key select depending on other choice in edit form (without jQuery)

Actually, django provided me with a neat solution.

When you look at the UserAdmin class within the django code, you’ll find a built-in way to handle a two-step creation process.

@admin.register(User)
class UserAdmin(admin.ModelAdmin):
    ...
    add_form = UserCreationForm
    ...

    def get_form(self, request, obj=None, **kwargs):
        """
        Use special form during user creation
        """
        defaults = {}
        if obj is None:
            defaults['form'] = self.add_form
        defaults.update(kwargs)
        return super().get_form(request, obj, **defaults)

When the attribute add_form is set and the object has no id yet (= we are creating it), it takes a different form than usual.

I wrapped this idea in an admin mixin like this:

class AdminCreateFormMixin:
    """
    Mixin to easily use a different form for the create case (in comparison to "edit") in the django admin
    Logic copied from `django.contrib.auth.admin.UserAdmin`
    """
    add_form = None

    def get_form(self, request, obj=None, **kwargs):
        defaults = {}
        if obj is None:
            defaults['form'] = self.add_form
        defaults.update(kwargs)
        return super().get_form(request, obj, **defaults)

Now, when I have dependent fields, I create a small form, containing all values independent of – in my case – company and a regular form containing everything.

@admin.register(Item)
class ItemAdmin(AdminCreateFormMixin, admin.ModelAdmin):
    form = ItemEditForm
    add_form = ItemAddForm
    ...

Now I can customise the querysets of the dependent field in my edit form:

class ItemEditForm(AdminDisabledCompanyFieldMixin, forms.ModelForm):
    class Meta:
        model = Item
        exclude = ()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.fields['contact_person'].queryset = ContactPerson.objects.filter(company=self.instance.company)

The only drawback is, that all dependent fields need to be nullable for the database. Otherwise you wouldn’t be able to save it in the creation process.

Luckily, you can tell django that a field is required in the form but not on database level with blank=False, null=True in the model declaration.

Hope this helps somebody else as well!

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top