pythondjangodjango-formsdjango-modeladmin

Specific Queryset for Input on Django ModelAdmin Change Form


I've got a 'Registration' object in place that users can create on the front end without issue.

It looks like this:

class Registration(models.Model):
    person = models.ForeignKey(Person, on_delete=models.PROTECT)
    course_detail = models.ForeignKey(CourseDetail, on_delete=models.PROTECT)
    camp_shirt = models.ForeignKey(CampShirt, on_delete=models.PROTECT)
    comments = models.CharField(max_length=200, blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return "%s" % (self.course_detail.course.camp)

When I am in the admin and click on a given Registration - it takes a while to load because there are thousands and thousands of Person objects.

For ease of use - there will never be a time when we would need to edit the 'person' associated with a given registration, so I would like to make the 'person' dropdown only show the selected user in the person queryset when editing from the django admin.

So when I go to http://myapp.com/admin/registration/23/change I want the form to only display the currently selected person as the only option in the dropdown.

My admin model looks like this:

class RegistrationAdmin(admin.ModelAdmin):
    list_display = ("person", "course_detail")

    class Meta:
        # I think this is what I do in order to override the default admin form? Not sure.
        form = RegistrationAdminForm

My RegistrationAdminForm looks like this:

class RegistrationAdminForm(forms.ModelForm):
    # course_detail, person, camp_shirt, comments
    person = forms.ModelChoiceField(queryset=Person.objects.filter(
        id=registration.person.id)
    )

    def __init__(self, registration, *args, **kwargs):
        super(RegistrationAdminForm, self).__init__(*args, **kwargs)
        self.fields['person'].queryset = Person.objects.filter(
            id=registration.person.id
        )

    class Meta:
        model = Registration
        fields = '__all__'

Main Question : How do I change the admin form so that a specific queryset is returned for one of the fields in the django admin?


Solution

  • If the person field will never be changed you can add the person field to readonly_fields, a select with all Person objects will not be rendered.

    class RegistrationAdmin(admin.ModelAdmin):
        list_display = ("person", "course_detail")
        readonly_fields = ("person", )
    

    Then you do not need your custom form. FYI when you want to add a custom form to a ModelAdmin you do not put it in Meta, you define it on the form itself

    class MyModelAdmin(admin.ModelAdmin):
        form = MyModelForm