Как установить динамическое отношение многих ко многим в factory boy со сквозной таблицей?

#django #many-to-many #factory-boy

#django #многие ко многим #factory-boy

Вопрос:

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

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

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

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

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

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

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

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

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

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

Ответ №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)
  

Комментарии:

1. Спасибо! Это мне немного помогло — я думаю, что на самом деле у меня был немного искажен метод сохранения в моей модели соединения, что вызывало у меня головную боль, я принимаю это, хотя в основном за те экстраординарные усилия, которые вы приложили — спасибо! — награда ваша.

2. Я думаю, что документы для factory-boy немного вводят в заблуждение, хотя конкретные инструкции по созданию отношений «многие ко многим» с сквозной таблицей на самом деле не указывают, что вы можете сделать это, как указано выше, но на самом деле вы можете.

3. Не беспокойтесь, рад, что это было полезно 🙂

4. Просто зашел, чтобы заглянуть и сказать спасибо. Это был идеальный пример!

5. Не беспокойся @Hanny, рад, что это помогло 🙂