Displaying a many-to-many relationship in Django

Viewed 37

Background: I have the following models for a visitor management app. Each site can have multiple visitors and each visitor can visit multiple sites. I am unable to find a way to show a list of visitors on each site or show a list of sites a visitor has visited. I have removed unnecessary fields.

.models.py

class Site(models.Model):
  name = models.CharField(max_length=100, unique=True)
  accomodation = models.BooleanField(default=False)
  visitors = models.ManyToManyField('Visitor', blank=True)

class Visitor(models.Model):
  name = models.CharField(max_length=50, null=False, blank=False)
  email = models.EmailField(max_length=200, null=False, blank=False, unique=True)
  ...

admin.py

class AdminArea(admin.AdminSite):
  
vms_admin = AdminArea(name='vms_admin')

@admin.register(Site, site=vms_admin)
class SiteAdmin(admin.ModelAdmin):
  fieldsets = (*removed*)
  list_display = [*removed*]
  list_filter = (*removed*)

@admin.register(Visitor, site=vms_admin)
class VisitorAdmin(admin.ModelAdmin):
  fieldsets = (*removed*)
  list_display = [*removed*]
  list_filter = (*removed*)

Django Admin

This is how the list of sites looks: Django Admin Site List

This is how the list of visitors look like: Django Admin Visitor List

Question How do I show the list of visitors for each site and vice versa? For example: If I click on Gingin Gravity Precinct, I want to see the list of visitors associated with it, in a table, below "site details". Similar to the list of visitors shown, but specific to the site. Django Admin Specific Site

1 Answers

First of all, I would make the related_name explicit in model Site. This is not strictly necessary, there is a default backward reference created by Django, I just never remember the name of that, so I like to be explicit.

visitors = models.ManyToManyField('Visitor', blank=True, related_name='sites')

I assume you want to see the list of sites when you click a single Visitor, in other words, on the visitor detail admin page. In that case, all you need to do is to specify the field sites in the VisitorAdmin. Vice versa to display visitors in the SiteAdmin. You will get a list of all visitors then, in which the active ones are selected.

class SiteAdmin(Admin):
    fieldsets = (
        ('Visitors', {'fields': ('visitors')})
    )

If you want to display a (possibly long) list of sites in the visitor list admin page, what you can do is to define a property on the Visitor model, and add that property to the list_fields.

class Visitor(Model):
    @property
    def list_of_sites(self):
        return self.sites.all().values_list(name, flat=True)

class SiteAdmin(Admin):
    list_display = ['name', 'accomodation', 'list_of_sites']
Related