Search code examples
pythondjangomany-to-many

Django - manager on ManyToMany relationship


I have 3 models

class Person(models.Model):
  name = models.CharField(max_length=128)

class Company(models.Model):
  name = models.CharField(max_length=128)
  members = models.ManyToManyField (Person, through = 'Membership', related_name = 'companies')

class Membership(models.Model):
  person = models.ForeignKey(Person, on_delete=models.CASCADE)
  company = models.ForeignKey(Company, on_delete=models.CASCADE)
  is_admin = models.BooleanField()

I can then call person.companies.all() to get the list of companies associated with person.

How do I create a manager to have the list of companies associated with person, but whose person is admin (is_admin = True)?


Solution

  • You can create a manager like the following:

    managers.py:

    from django.db import models
    
    class AdminCompaniesManager(models.Manager):
        def get_queryset(self):
            return super().get_queryset().companies.filter(membership__is_admin=True)
    

    and then in your Person model (please remind the objects manager):

    class Person(models.Model):
        name = models.CharField(max_length=128)
        objects = models.Manager()
        administered_companies = AdminCompaniesManager()
    

    Now you can easily call the following (e.g. in your views):

    my_person.administered_companies.all()
    

    PS: a very efficient option (e.g. if you are in a view and you need the list of company ids by a given person) is to query the membership model directly, so you can optimize the query for data retrieval from DB avoiding the joins:

    Membership.objects.filter(is_admin=True, person=person).values_list('company_id')