Reputation: 311
Apologies if this has been answered, I'm uncertain how to word it so couldn't find an existing solution.
Let's say I have a basic ManyToMany relationship as shown here:
class Topping(models.Model):
name = models.Charfield()
class Pizza(models.Model):
toppings = models.ManyToMany(Topping)
That's simple and great, however in the project I'm working on, I want to be able to select the same Topping more than once. For example, ["pepperoni", "mushroom", "onion"] would be a perfectly good result, but I need to allow something like ["pepperoni", "pepperoni", "pepperoni"]
I've tried using a intermediate class, where Pizza has a ManyToMany to Topping, which is just a foreignkey to ToppingType, which is the Charfield -
class ToppingType(models.Model):
name = models.Charfield()
class Topping(models.Model):
type = models.ForeignKey(ToppingType)
class Pizza(models.Model):
toppings = models.ManyToMany(Topping)
and that works, however it means if one day I create something with five "pepperoni" selections, I then permanently have five copies of pepperoni as a Topping in my database.
As I mentioned at the top, I'm sure there's a fairly clean solution, but I've had trouble figuring out how to phrase my searches.
Upvotes: 1
Views: 144
Reputation: 476594
You don't need a ManyToManyField
to the Topping
, you can let the Topping
act as a junction table [wiki] and thus specify this as through=…
model [Django-doc]:
class ToppingType(models.Model):
name = models.Charfield(max_length=255, unique=True)
class Pizza(models.Model):
toppings = models.ManyToMany(
ToppingType,
through='Topping'
)
class Topping(models.Model):
type = models.ForeignKey(ToppingType, on_delete=models.CASCADE)
pizza = models.ForeignKey(Pizza, on_delete=models.CASCADE)
Here you thus create a Topping
object per pizza
per topping. But you do not duplicate the ToppingType
s. In fact if you do not specify a through=…
, then Django creates a (hidden) model that acts as the junction model.
You thus can create for example as ToppingType
s pepperoni and onion:
pepperoni = ToppingType.objects.create(name='pepperoni')
onion = ToppingType.objects.create(name='onion')
and then for a pizza add pepperoni
, onion
, and pepperoni
:
my_pizza = Pizza.objects.create()
Topping.objects.create(pizza=my_pizza, type=pepperoni)
Topping.objects.create(pizza=my_pizza, type=onion)
Topping.objects.create(pizza=my_pizza, type=pepperoni)
Upvotes: 1