Как установить динамику «многие ко многим» в фабричном мальчике со сквозной таблицей?

У меня проблемы с настройкой отношений «многие ко многим» с набором моделей django в factory boy, используя сквозные отношения. У меня есть куча рецептов и ингредиентов. Существует связь «многие ко многим» между рецептами и ингредиентами через модель, которая устанавливает количество. У меня есть фабрики для каждой модели, но я не могу их соединить.

упрощенные модели.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)

упрощенные factory.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

Я пытался возиться с factory.RelatedFactory, но на самом деле ничего не добился. В идеале я просто хочу сделать следующее:

recipe = RecipeFactory(name="recipe1")
ingredient = IngredientFactory(name="ingredient1")
ri = RecipeIngredientFactory(recipe=recipe, ingredient=ingredient)

Это, однако, не устанавливает отношение многие ко многим с обеих сторон, а также, по-видимому, не может создать саму модель рецепта. Кто-нибудь знает способ сделать это?

Редактировать:

Я также пробовал:

class RecipeWith3Ingredients(RecipeFactory):
    ingredient1 = factory.RelatedFactory(RecipeIngredientFactory, 'recipe')
    ingredient2 = factory.RelatedFactory(RecipeIngredientFactory, 'recipe')
    ingredient3 = factory.RelatedFactory(RecipeIngredientFactory, 'recipe')

Но не могу понять, как я буду создавать эти объекты с уже существующим рецептом и набором ингредиентов.


person bharling    schedule 11.10.2016    source источник


Ответы (1)


Я только что воссоздал эту настройку, и я изо всех сил пытаюсь понять, в чем проблема. Вот несколько тестов, которые демонстрируют, что все вроде бы работает хорошо? Или я неправильно понимаю вопрос?

# 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)
person tdsymonds    schedule 04.11.2016
comment
Благодарю вас! Это немного помогло мне - я думаю, что на самом деле у меня был немного искаженный метод сохранения в моей модели соединения, что вызывало у меня головную боль, я принимаю это, хотя в основном за экстраординарные усилия, которые вы приложили - спасибо! - награда твоя. - person bharling; 07.11.2016
comment
Я думаю, что документы для factory-boy немного вводят в заблуждение, хотя конкретные инструкции по созданию экземпляров отношений «многие ко многим» со сквозной таблицей на самом деле не указывают, что вы можете сделать это, как указано выше, но на самом деле вы можете. - person bharling; 07.11.2016
comment
Ничего страшного, рад, что пригодилось :) - person tdsymonds; 07.11.2016
comment
Просто зашел зайти и сказать спасибо. Это был идеальный пример! - person Hanny; 07.01.2019
comment
Не беспокойтесь @Hanny, рад, что это помогло :) - person tdsymonds; 08.01.2019
comment
Отлично, на это можно сослаться здесь factoryboy.readthedocs.io/en/latest/ - person Pierre Cordier; 05.06.2020