Many-To-Many Relationship (ManyToManyField)

Django Model Relations — 3

Emre Cevik
Python | Django & Rest

--

Before learning about this, you may want to take a look at one-to-one and one -to-many relationships.

Many-To-Many Relationship

Multiple records in a table are associated with multiple records in another table.

- Many car models can run on same fuel type.
- Car model can run on diffrent fuel types.

|--------|--------|
|CarModel|FuelType|
|--------|--------|
| C180 | Diesel |
| C180 | Gas |
| C200 | Diesel |
| C200 | Hybrid |
| C220 | Gas |
|--------|--------|

Add CarModel

$ from modelrelations.models import CarModel, FuelType$ c180 = CarModel.objects.create(name=”C180")
$ c200 = CarModel.objects.create(name="C200")
$ CarModel.objects.all()<QuerySet [
<CarModel: C180>,
<CarModel: C200>
]>

Add FuelType

$ gas = FuelType.objects.create(name=”Gas”)
$ diesel = FuelType.objects.create(name=”Diesel”)
$ hybrid = FuelType.objects.create(name=”Hybrid”)
$ FuelType.objects.all()<QuerySet [
<FuelType: Gas>,
<FuelType: Diesel>,
<FuelType: Hybrid>
]>

Associate the CarModel with a FuelType

$ c180.fueltype.add(gas)
$ c180.fueltype.all()
<QuerySet [
<FuelType: Gas>
]>

Adding a second time is OK, it will not duplicate the relation:

$ c180.fueltype.add(gas)
$ c180.fueltype.all()
<QuerySet [
<FuelType: Gas>
]>

We can associate the CarModel with more FuelType:

$ c180.fueltype.add(diesel)
$ c180.fueltype.all()
<QuerySet [
<FuelType: Gas>,
<FuelType: Diesel>
]>

We can associate the CarModel with multiple FuelType in one step:

$ c200.fueltype.add(gas, diesel, hybrid)
$ c200.fueltype.all()
<QuerySet [
<FuelType: Gas>,
<FuelType: Diesel>,
<FuelType: Hybrid>
]>

Adding an object of the wrong type raises TypeError:

$ c200.fueltype.add(c200)TypeError: ‘FuelType’ instance expected, got <CarModel: C200>

Create and add a FuelType to a CarModel in one step using create():

$ c200.fueltype.create(name="Bio Diesel")<FuelType: Bio Diesel>$ c200.fueltype.all()<QuerySet [
<FuelType: Gas>,
<FuelType: Diesel>,
<FuelType: Hybrid>,
<FuelType: Bio Diesel>
]>
$ FuelType.objects.all()<QuerySet [
<FuelType: Gas>,
<FuelType: Diesel>,
<FuelType: Hybrid>,
<FuelType: Bio Diesel>
]>

Associate the FuelType with a CarModel

$ gas.carmodel_set.all()<QuerySet [
<CarModel: C180>,
<CarModel: C200>
]>
$ FuelType.objects.get(name="Gas").carmodel_set.all()<QuerySet [
<CarModel: C180>,
<CarModel: C200>
]>

related_name will be the attribute of the related object that allows you to go ‘backwards’ to the model.

You can access the “CarModel” instances that are related to your “FuelType“ instance by going fueltype_instance.models.all(). You can no longer use carmodel_set.

$ gas.models.all()<QuerySet [
<CarModel: C180>,
<CarModel: C200>
]>

Filtering Records

Many-to-many relationships can be queried using lookups across relationships:

$ CarModel.objects.filter(fueltype__name__startswith="G")<QuerySet [
<CarModel: C180>,
<CarModel: C200>
]>

Reverse m2m queries are supported :

$ FuelType.objects.filter(models__name__startswith="C")<QuerySet [
<FuelType: Gas>,
<FuelType: Diesel>,
<FuelType: Gas>,
<FuelType: Diesel>,
<FuelType: Hybrid>,
<FuelType: Bio Diesel>
]>
$ FuelType.objects.filter(models__name__startswith="C").distinct()<QuerySet [
<FuelType: Gas>,
<FuelType: Diesel>,
<FuelType: Hybrid>,
<FuelType: Bio Diesel>
]>

Delete Records

Removing FuelType from a CarModel:

$ c180.fueltype.all()<QuerySet [
<FuelType: Gas>,
<FuelType: Diesel>
]>
$ c180.fueltype.remove(gas)
$ c180.fueltype.all()
<QuerySet [
<FuelType: Diesel>
]>

Delete some car models:

$ cars = CarModel.objects.filter(fueltype__name__startswith="B")
$ cars
<QuerySet [
<CarModel: C200>
]>
$ cars.delete()
(5, {'modelrelations.CarModel_fueltype': 4, 'modelrelations.CarModel': 1})
$ CarModel.objects.filter(fueltype__name__startswith="B")
<QuerySet []>

Relation sets can be cleared:

$ c180.fueltype.clear()
$ c180.fueltype.all()
<QuerySet []>

Relation sets can be set:

$ c180.fueltype.set([hybrid])
$ c180.fueltype.all()
<QuerySet [
<FuelType: Hybrid>
]>

You might need a custom “through” model

Django will automatically generate a table to manage many-to-many relationships. You might need a custom “through” model. The most common use for this option is when you want to associate extra data with a many-to-many relationship.

In the next part, we’ll learn polymorphic relations.

If you want to learn more about Django, do check out the documentation, django rest framework website and make sure to check out part of this series!

--

--