How to change ForeignKey display text in the Django Admin?

Viewed 44752

How can I change the display text in a <select> field while selecting a field which is a ForeignKey?

I need to display not only the name of ForeignKey, but also the name of its parent.

9 Answers

You can also accomplish this straight from your admin.ModelAdmin instance using label_from_instance. For example:

class InvoiceAdmin(admin.ModelAdmin):

    list_display = ['person', 'id']

    def get_form(self, request, obj=None, **kwargs):
        form = super(InvoiceAdmin, self).get_form(request, obj, **kwargs)
        form.base_fields['person'].label_from_instance = lambda inst: "{} {}".format(inst.id, inst.first_name)
        return form


admin.site.register(Invoice, InvoiceAdmin)

Building on the other answers, here's a small example for those dealing with a ManyToManyField.

We can override label_from_instance directly in formfield_for_manytomany():

class MyAdmin(admin.ModelAdmin):
    def formfield_for_manytomany(self, db_field, request, **kwargs):
        formfield = super().formfield_for_manytomany(db_field, request, **kwargs)
        if db_field.name == 'person':
            # For example, we can add the instance id to the original string
            formfield.label_from_instance = lambda obj: f'{obj} ({obj.id})'
        return formfield

This would also work for formfield_for_foreignkey() or formfield_for_dbfield().

From the ModelChoiceField docs:

The __str__() method of the model will be called to generate string representations of the objects for use in the field’s choices. To provide customized representations, subclass ModelChoiceField and override label_from_instance. This method will receive a model object and should return a string suitable for representing it.

An alternative to the first answer:

class InvoiceAdmin(admin.ModelAdmin):

    class CustomModelChoiceField(forms.ModelChoiceField):
         def label_from_instance(self, obj):
             return "%s %s" % (obj.first_name, obj.last_name)

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == 'person':
            return self.CustomModelChoiceField(queryset=Person.objects)

        return super(InvoiceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

By overriding formfield_for_foreignkey(), you can display the combination of foreign key and its parent to Django Admin without creating a custom "forms.ModelChoiceField" and a custom "forms.ModelForm" as shown below:

@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        formfield = super().formfield_for_foreignkey(db_field, request, **kwargs)
        if db_field.name == "my_field":
            formfield.label_from_instance = lambda obj: f'{obj} ({obj.my_parent})'
        return formfield

In addition, this code below is with a custom "forms.ModelForm":

class MyModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
                                 
        self.fields['my_field'].label_from_instance = lambda obj: f'{obj} ({obj.my_parent})'

@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
    form = MyModelForm

And, this code below is with a custom "forms.ModelChoiceField" and a custom "forms.ModelForm":

class CustomModelChoiceField(forms.ModelChoiceField):
    def label_from_instance(self, obj):
        return f'{obj} ({obj.my_parent})'

class MyModelForm(forms.ModelForm):
    my_field = CustomModelChoiceField(queryset=MyField.objects.all())

@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
    form = MyModelForm
Related