Home > OS >  How to setup search in django-admin changelists in models related through ForeignKeys?
How to setup search in django-admin changelists in models related through ForeignKeys?

Time:06-12

I have created the following four model classes:

class InfrastructureModel(models.Model):
    ENTRY_ACTIVE_YES_NO_CHOICES = (
        (True, 'Yes'),
        (False, 'No'))

    entryActiveYesNo = models.BooleanField(r"Is this database entry still active? (YES/NO)",
                                      null=False,
                                      blank=False,
                                      unique=False,
                                      choices=ENTRY_ACTIVE_YES_NO_CHOICES,
                                      help_text=r"Is this database entry still active? If it's been changed/modified to something else, mark this as False.")

class AirportAdministrativeData(InfrastructureModel):
    officialAirportName=models.CharField(r"Airport's Official Name",
                                         max_length=100,
                                         null=False, 
                                         blank=False, 
                                         unique=True,
                                         help_text=r"Offical name of the Airport")
    def __str__(self):
        return self.officialAirportName

class AirportAlternateName(InfrastructureModel):
    parentAirport=models.ForeignKey(AirportAdministrativeData,on_delete=models.RESTRICT,limit_choices_to={'entryActiveYesNo': True},verbose_name="Parent Airport",related_name='AirportOfficialName')
    alternateAirportName=models.CharField(r"Airport's Alternate Name",
                                          max_length=100, 
                                          null=False, 
                                          blank=False,
                                          unique=True,
                                          help_text=r"Alternate name of the Airport, if any.")

class AirportLocation(InfrastructureModel):
    parentAirport=models.ForeignKey(AirportAdministrativeData,on_delete=models.RESTRICT,limit_choices_to={'entryActiveYesNo': True},verbose_name="Parent Airport")
    latitude=models.DecimalField(max_digits=9, decimal_places=6)
    longitude=models.DecimalField(max_digits=9, decimal_places=6)
    airportAddress=models.CharField(r"Airport's Address",
                                          max_length=200, 
                                          null=False, 
                                          blank=False,
                                          unique=True,
                                          help_text=r"Airport's Address")
    airportState=models.ForeignKey(State,on_delete=models.RESTRICT,limit_choices_to={'entryActiveYesNo': True},verbose_name="State")

And their corresponding Admin classes are as follows:

class AirportAdministrativeDataAdmin(admin.ModelAdmin):
    fields = ['officialAirportName', 'entryActiveYesNo']
    list_display = ('officialAirportName', 'entryActiveYesNo')
    search_fields = ['officialAirportName']
    search_help_text="Seach here for offical/alternate name of any airport in the Database."
class AirportAlternateNameAdmin(admin.ModelAdmin):
    fields = ['parentAirport', 'alternateAirportName', 'entryActiveYesNo']
    list_display = ('parentAirport', 'alternateAirportName', 'entryActiveYesNo')
    search_fields = ['alternateAirportName']
    search_help_text="Seach here for offical/alternate name of any airport in the Database."
class AirportLocationAdmin(admin.ModelAdmin):
    fields = ['parentAirport', 'latitude', 'longitude', 'airportAddress', 'airportState', 'entryActiveYesNo']
    list_display = ('parentAirport', 'latitude', 'longitude', 'airportAddress', 'airportState', 'entryActiveYesNo')
    #search_fields = ['parentAirport']
    search_help_text="Seach here for offical/alternate name of any airport in the Database."

And their corresponding admin site registrations are as follows:

admin.site.register(AirportAdministrativeData,AirportAdministrativeDataAdmin)
admin.site.register(AirportAlternateName, AirportAlternateNameAdmin)
admin.site.register(AirportLocation, AirportLocationAdmin)

I want to be able to search in the change lists of these respective models using either of the following fields:

  1. AirportAdministrativeData model’s officialAirportName field, or,
  2. AirportAlternateName model’s alternateAirportName field.

and return the corresponding search entry.

I have read several articles such as django documentation on ForeignKey.related_name and this stackoverflow q&a. But my novice self is still struggling in being able to implement this feature. Please help!

CodePudding user response:

To add a search field from a related model you need to use the same notation as with filter(), i.e. a path with names of the fields separated with double-underscores.

First search field

To add AirportAdministrativeData.officialAirportName as search field to the change list of AirportLocation you add this: 'parentAirport__officialAirportName.

Second search field

For the 2nd search field to the alternate name you need to go one level deeper and use the "related name", which is the reverse relation from AirportAdministrativeDataAdmin to AirportAlternateNameAdmin:

'parentAirport__AirportOfficialName__alternateAirportName'

Result

Search fields are combined with OR by default, so you can add the 2nd search field and it will filter objects which match one or the other:

class AirportLocationAdmin(admin.ModelAdmin):
    ...
    search_fields = [
        'parentAirport__officialAirportName',
        'parentAirport__AirportOfficialName__alternateAirportName'
    ]
    ...

Two additional suggestions if I may:

  • In Python properties of a method are supposed to be in snake case, like you see in the Admin classes. So I would suggest to rename all field names of your models and also the "related_name" definitions. See PEP8 for more infos about the official coding style for Python
  • the "r" in front of your strings is obsolete. It means that you are defining regular expressions strings, which would not make sense here. Would suggest to remove the "r", you do not need it.
  • Related