Search code examples
djangodjango-modelsmany-to-many

Django get all objects whose many to many filed contains a filter


I have 3 models:

  • City: indicating a city
  • Titles: indicating job titles
  • Contacts: indicating people. A person can have multiple titles and multiple cities

Now, given an city object and a title object, I want to query for all people that have those objects in their instance. Basically something like "All Lawyers in NY".

class City(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name=models.CharField(max_length=40)
    state=models.CharField(max_length=15)

class Title(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title_name=models.CharField(max_length=40)

class Contact(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name_surname = models.CharField(max_length=60)
    title = models.ManyToManyField(Title)
    city = models.ManyToManyField(City)

Solution

  • You can filter with:

    Contract.objects.filter(
        title__title_name='Lawyer',
        city__name='NY'
    )

    But, the modeling does not make much sense, since here a Contract relates to multiple Titles and multiple Citys, but it is unclear what title belongs to what city.

    Imagine that we have a person who is a software engineer in New York, and a barman in Los Angeles, then the modelling can not make a distinction between this person, and a person who is a barman in New York, and a software engineer in Los Angeles.

    We can model this with a Person and a Contract that refers to a city and a title, so:

    class City(models.Model):
        id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
        name=models.CharField(max_length=40)
        state=models.CharField(max_length=15)
    
    class Title(models.Model):
        id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
        title_name=models.CharField(max_length=40)
    
    class Person(models.Model):
        id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=Falsee)
        name_surname = models.CharField(max_length=60)
    
    class Contact(models.Model):
        id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
        person = models.ForeignKey(Person, on_delete=models.CASCADE)
        title = models.ForeignKey(Title, on_delete=models.CASCADE)
        city = models.ForeignKey(City, on_delete=models.CASCADE)

    Then we can determine such person with:

    Person.objects.filter(
        contract__title__title_name='Lawyer',
        contract__city__name='NY'
    )

    Then we thus retrieve all Persons that have at least a Contract with lawyer as title_name, and NY as name for the city.