Cyzanfar
Cyzanfar

Reputation: 7136

ManyToMany through Queryset Django 2.2

I have 3 models and their relation:

class Itinerary(models.Model):
    origin_dest = models.CharField(max_length=200, blank=False, default="")
    travelers = models.ManyToManyField('Traveler')
    flights = models.ManyToManyField('Flight')

class Traveler(models.Model):
    pass

class Flight(models.Model):
    pass

An Itinerary has one or many flights (stop vs nonstop) and a flight can belong to more than one itinerary. Also, a traveler can have many itineraries and an itinerary can have many travelers. Here is why:

Let's say traveler 1 has a flight from JFK-LAX which has 1 stop in MIA. Then the Itinerary will have 2 flights (JFK-MIA, MIA-LAX). Now let's say we have another person, traveler 2 who has an itinerary from MIA-LAX which is the same flight as traveler 1 going from MIA-JFK (different itinerary instance but same flight shared). Therefore in this case, there are 2 itinerary where a single flight (MIA-LAX) belongs to those two itineraries, hence the ManyToMany relationship.

Now what I'd like to do ideally is query all travelers that are on a particular flight through the itinerary model. Somethind like this:

flight.itinerary_set.travelers #=> return the number of travelers that are on that specific flight.

Upvotes: 0

Views: 246

Answers (1)

kungphu
kungphu

Reputation: 4849

You might want to have an intermediate object, e.g. Booking, that's always a single flight and a single traveler. That way there's no potential for confusion regarding which traveler is on which flight, which seems like a pretty important distinction, unless for some reason that's not relevant in your use case.

Example models for that case:

from django.db import models


class Traveler(models.Model):
    name = models.TextField()

    def __str__(self):
        return self.name


class Flight(models.Model):
    number = models.TextField()

    def __str__(self):
        return self.number


class Itinerary(models.Model):
    pass


class Booking(models.Model):
    itinerary = models.ForeignKey(
        Itinerary,
        related_name='bookings',
        on_delete=models.CASCADE,
    )

    traveler = models.ForeignKey(
        Traveler,
        related_name='bookings',
        on_delete=models.CASCADE,
    )
    flight = models.ForeignKey(
        Flight,
        related_name='bookings',
        on_delete=models.CASCADE,
    )

Partial output from test case is below. Note that this creates two separate itineraries with independent bookings on the same flight.

>>> from itineraries.models import Traveler, Flight, Itinerary, Booking
>>> 
>>> f = Flight.objects.create(number='JAL1')
>>> 
>>> Booking.objects.create(
...     itinerary=Itinerary.objects.create(),
...     traveler=Traveler.objects.create(name='kungphu'),
...     flight=f,
... )
<Booking: Booking object (5)>
>>> 
>>> Booking.objects.create(
...     itinerary=Itinerary.objects.create(),
...     traveler=Traveler.objects.create(name='Cyzanfar'),
...     flight=f,
... )
<Booking: Booking object (6)>
>>> 
>>> # Travelers on flight f:
>>> for t in Traveler.objects.filter(bookings__flight=f):
...     print(t)
... 
kungphu
Cyzanfar

Upvotes: 1

Related Questions