Sunday, July 4, 2021

Django Model relationship custom through model

The example used in the Django docs is of a Group, Person, and Membership relationship. A group can have many people as members, and a person can be part of many groups, so the Group model has a ManyToManyField that points to Person. Then, a Membership model contains ForeignKeys to both Person and Group, and can store extra information about a person's membership in a specific group, like the date they joined, who invited them, etc.


Using our existing models, we can create all kinds of pizzas with a wide range of toppings. But we can't make a pizza like "Super Pepperoni" that contains double the usual amount of pepperonis. We can't add pepperoni to a pizza more than once:



class ToppingAmount(models.Model):


    REGULAR = 1

    DOUBLE = 2

    TRIPLE = 3

    AMOUNT_CHOICES = (

        (REGULAR, 'Regular'),

        (DOUBLE, 'Double'),

        (TRIPLE, 'Triple'),

    )


    pizza = models.ForeignKey('Pizza', related_name='topping_amounts', on_delete=models.SET_NULL, null=True)

    topping = models.ForeignKey('Topping', related_name='topping_amounts', on_delete=models.SET_NULL, null=True, blank=True)

    amount = models.IntegerField(choices=AMOUNT_CHOICES, default=REGULAR)



Now, add the through option to the toppings field on the Pizza model:

class Pizza(models.Model):

   ...

    toppings = models.ManyToManyField('Topping', through='ToppingAmount', related_name='pizzas')



If specify a through model and does not use it while associating, it will give error 


>> super_pep = Pizza.objects.create(name='Super Pepperoni')

>> pepperoni = Topping.objects.create(name='pepperoni')

>> super_pep.toppings.add(pepperoni)

Traceback (most recent call last):

...

AttributeError: Cannot use add() on a ManyToManyField which specifies an intermediary model. 

Use pizzas.ToppingAmount's Manager instead.



Using a custom "through" model forces us to use that model to associate the pizza and toppings.

super_pep_amount = ToppingAmount.objects.create(pizza=super_pep, topping=pepperoni, amount=ToppingAmount.DOUBLE)


for top_amt in ToppingAmount.objects.filter(pizza=super_pep):

    print(top_amt.topping.name, top_amt.get_amount_display())


pepperoni Double



A through model is also useful for relationships between players and teams; the through model could contain information about the players' positions, jersey numbers, and dates they joined the team. A through model joining movie theatres and films could contain the number of screens the film is showing on and the start and end run dates. Students' relationships to their Degree Programs could track information like GPA, whether the program is the student's major or minor, whether it's a double major, and start/end semesters the student was in a program.





References:

https://www.revsys.com/tidbits/tips-using-djangos-manytomanyfield/#:~:text=The%20example%20used%20in%20the,ManyToManyField%20that%20points%20to%20Person%20.

No comments:

Post a Comment