Search code examples
pythondjangodjango-modelsforeign-keysdjango-admin

Django admin, how can I add the FK selector in a StackedInline record


Given the following models:

class Person(models.Model):
    name = models.CharField(max_length=50)
    family = models.ForeignKey(
        "Family", related_name="members", on_delete=models.CASCADE
    )

    def __str__(self) -> str:
        return self.name

class Family(models.Model):
    name = models.CharField(max_length=50)

    def __str__(self) -> str:
        return self.name

And the following admin settings:

class PersonAdmin(admin.StackedInline):
    model = Person
    list_display = ("name", "family")

    extra = 0

class FamilyAdmin(admin.ModelAdmin):
    model = Family
    list_display = ("name",)

    inlines = [PersonAdmin]

    extra = 0

admin.site.register(Family, FamilyAdmin)

How, from the Family edit page, can I change the family of a person?

Say I have Person A belonging to Family 1. I would like to be able to migrate this person to another family from the family edit page.

Unfortunately, even if I explicitly specify the fields in the PersonAdmin class, the dropdown won't show.


Solution

  • You can add a custom form to the PersonAdmin class and override the __init__() method to dynamically add a ModelChoiceField for selecting the new family of a person.

    Try this:

    
    from django import forms
    from django.contrib import admin
    
    from .models import Family, Person
    
    
    class PersonForm(forms.ModelForm):
        new_family = forms.ModelChoiceField(
            queryset=Family.objects.all(),
            required=True,
            label="New Family",
        )
    
        class Meta:
            model = Person
            fields = ["name", "family", "new_family"]
    
    
    class PersonAdmin(admin.StackedInline):
        model = Person
        list_display = ("name", "family")
        extra = 0
        form = PersonForm  # Here we Added custom form
    
    
    class FamilyAdmin(admin.ModelAdmin):
        model = Family
        list_display = ("name",)
        inlines = [PersonAdmin]
        extra = 0
    
    
    admin.site.register(Family, FamilyAdmin)