Following Relationships
Employees reviewing Peanut's adoption need to see Peanut's applications, and each application needs to say which animal it's for. Our foreign key links the two tables, and the ORM can follow it in either direction.
Following a Foreign Key Forward
>>> application = AdoptionApplication.objects.get(applicant_name="Maria")
>>> application.animal
<Animal: Peanut>
application.animal is the Animal the foreign key points at. Reading it the first time runs a query for that animal.
Following a Foreign Key Backward
>>> peanut = Animal.objects.get(name="Peanut")
>>> peanut.applications.count()
2
>>> peanut.applications.values_list("applicant_name", flat=True)
<QuerySet ['Maria', 'Aisha']>
applications is the foreign key's related_name. Every Animal has it, and it works like Animal.objects, limited to that animal's applications.
Our applications don't have a __str__ yet, so the shell would show each one as AdoptionApplication object with its id. values_list() returns the values of the fields we name instead of model instances. With flat=True and one field, each value comes back alone, not in a tuple.
Lookups Across a Relationship
Two underscores also follow a foreign key inside a lookup. animal__species is the species of the application's animal:
>>> AdoptionApplication.objects.filter(animal__species="cat").values_list("applicant_name", flat=True)
<QuerySet ['Maria', 'Aisha']>
A lookup can follow the relationship backward too, through the related_name:
>>> Animal.objects.filter(applications__applicant_name="Aisha")
<QuerySet [<Animal: Peanut>]>
Repeated rows
A backward lookup returns one row for each matching application, so an animal with two comes back twice. distinct() removes the repeats:
>>> Animal.objects.filter(applications__applicant_name__in=["Maria", "Aisha"])
<QuerySet [<Animal: Peanut>, <Animal: Peanut>]>
>>> Animal.objects.filter(applications__applicant_name__in=["Maria", "Aisha"]).distinct()
<QuerySet [<Animal: Peanut>]>