I'm having problems setting up a many to many relationship with a set of django models in factory boy, using a through relationship. I have a bunch of recipes and ingredients. There is a many-to-many relation between Recipes and Ingredients through a model that sets a quantity. I have factories for each model but cannot get them to link up.
simplified models.py:
class Ingredient(models.Model):
name = models.CharField(max_length=40)
class Recipe(models.Model):
name = models.CharField(max_length=128)
ingredients = models.ManyToManyField(Ingredient, through='RecipeIngredient')
class RecipeIngredient(models.Model):
recipe = models.ForeignKey(Recipe)
ingredient = models.ForeignKey(Ingredient)
quantity = models.IntegerField(default=1)
simplified factories.py
class RecipeFactory(factory.django.DjangoModelFactory):
class Meta:
model = Recipe
class IngredientFactory(factory.django.DjangoModelFactory):
class Meta:
model = Ingredient
class RecipeIngredientFactory(factory.django.DjangoModelFactory):
class Meta:
model = RecipeIngredient
recipe = factory.SubFactory(RecipeFactory)
ingredient = factory.SubFactory(IngredientFactory)
quantity = 1
I've tried messing with factory.RelatedFactory but haven't really got anywhere. Ideally I just want to be able to do the following:
recipe = RecipeFactory(name="recipe1")
ingredient = IngredientFactory(name="ingredient1")
ri = RecipeIngredientFactory(recipe=recipe, ingredient=ingredient)
Doing this though does not set the many to many relation on either side, and also seems to fail to create the recipeingredient model itself. Does anyone know of a way to do this?
Edit:
I've also tried:
class RecipeWith3Ingredients(RecipeFactory):
ingredient1 = factory.RelatedFactory(RecipeIngredientFactory, 'recipe')
ingredient2 = factory.RelatedFactory(RecipeIngredientFactory, 'recipe')
ingredient3 = factory.RelatedFactory(RecipeIngredientFactory, 'recipe')
But cant get my head around how I'd create these objects with a pre-existing recipe and set of ingredients.
I've just recreated this setup and I'm struggling to see what the problem is. Here are several tests that demonstrate that all seems to be working well? Or am I misunderstanding the question?
# create recipe ingredient and recipe ingredient
recipe = RecipeFactory(name="recipe1")
ingredient = IngredientFactory(name="ingredient1")
recipe_ingredient = RecipeIngredientFactory(recipe=recipe, ingredient=ingredient)
# recipe created?
r = Recipe.objects.all().first()
self.assertEqual(r, recipe)
# ingredient created?
i = Ingredient.objects.all().first()
self.assertEqual(i, ingredient)
# recipe ingredient created?
ri = RecipeIngredient.objects.all().first()
self.assertEqual(ri, recipe_ingredient)
# test many to many
self.assertEqual(ri, r.recipeingredient_set.all()[0])
self.assertEqual(ri, i.recipeingredient_set.all()[0])
# add a new ingredient to recipe 1
ingredient2 = IngredientFactory(name='ingredient2')
recipe_ingredient2 = RecipeIngredientFactory(recipe=recipe, ingredient=ingredient2)
# test many to many
self.assertTrue(recipe_ingredient in r.recipeingredient_set.all())
self.assertTrue(recipe_ingredient2 in r.recipeingredient_set.all())
# create a pre-existing recipe and a set of ingredients
pizza_recipe = RecipeFactory(name='Pizza')
cheese_on_toast_recipe = RecipeFactory(name='Cheese on toast')
cheese_ingredient = IngredientFactory(name='Cheese')
tomato_ingredient = IngredientFactory(name='Tomato')
pizza_base_ingredient = IngredientFactory(name='Pizza base')
toast_ingredient = IngredientFactory(name='Toast')
# now put together
RecipeIngredientFactory(recipe=pizza_recipe, ingredient=cheese_ingredient)
RecipeIngredientFactory(recipe=pizza_recipe, ingredient=tomato_ingredient)
RecipeIngredientFactory(recipe=pizza_recipe, ingredient=pizza_base_ingredient)
RecipeIngredientFactory(recipe=cheese_on_toast_recipe, ingredient=cheese_ingredient)
RecipeIngredientFactory(recipe=cheese_on_toast_recipe, ingredient=toast_ingredient)
# test pizza recipe
pizza_ingredients = [cheese_ingredient, tomato_ingredient, pizza_base_ingredient]
pr = Recipe.objects.get(name='Pizza')
for recipe_ingredient in pr.recipeingredient_set.all():
self.assertTrue(recipe_ingredient.ingredient in pizza_ingredients)
# test cheese on toast recipe
cheese_on_toast_ingredients = [cheese_ingredient, toast_ingredient]
cotr = Recipe.objects.get(name='Cheese on toast')
for recipe_ingredient in cotr.recipeingredient_set.all():
self.assertTrue(recipe_ingredient.ingredient in cheese_on_toast_ingredients)
# test from ingredients side
cheese_recipes = [pizza_recipe, cheese_on_toast_recipe]
ci = Ingredient.objects.get(name='Cheese')
for recipe_ingredient in ci.recipeingredient_set.all():
self.assertTrue(recipe_ingredient.recipe in cheese_recipes)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With