Many-to-many relationship in factory_boy?

I am trying to test the many-to-many relationship between two Django models using factory_boy. The factory_boy documentation doesn't seem to discuss this, and it's hard for me to understand what I'm doing wrong. When I run the first test, I get the error "AttributeError: object" Pizza "does not have the attribute" topping "". I get a similar error for the second test.

When I run tests in the debugger, I can see the "toppings" object, but it does not understand how to get a name from it. Did I define the PizzaFactory _prepare method correctly? How to access a name in one table from another table when you have a many-to-many relationship?

Thanks.

models.py:

from django.db import models class Topping(models.Model): name = models.CharField(max_length=50) def __unicode__(self): return self.name class Pizza(models.Model): name = models.CharField(max_length=100) toppings = models.ManyToManyField(Topping) def __unicode__(self): return self.name 

factories.py:

 import factory from models import Topping, Pizza class ToppingFactory(factory.Factory): name = 'mushrooms' class PizzaFactory(factory.Factory): name = 'Vegetarian' @classmethod def _prepare(cls, create, **kwargs): topping = ToppingFactory() pizza = super(PizzaFactory, cls)._prepare(create, **kwargs) pizza.toppings.add(topping) return pizza 

tests.py

 from django.test import TestCase import factory from app.models import Topping, Pizza from app.factories import ToppingFactory, PizzaFactory class FactoryTestCase(TestCase): def test_pizza_has_mushrooms(self): pizza = PizzaFactory() self.assertTrue(pizza.topping.name, 'mushrooms') def test_mushrooms_on_pizza(self): topping = ToppingFactory() self.assertTrue(topping.pizza.name, 'Vegetarian') 
+8
unit-testing django-testing factory-boy
source share
2 answers

I believe you need to use the @factory.post_generation decorator:

 class PizzaFactory(factory.Factory): name = 'Vegetarian' @factory.post_generation def toppings(self, create, extracted, **kwargs): if not create: # Simple build, do nothing. return if extracted: # A list of groups were passed in, use them for topping in extracted: self.toppings.add(topping) 

Then you would call it in test.py pizza = PizzaFactory.create(toppings=(topping1, topping2, tooping3))

From https://factoryboy.readthedocs.org/en/latest/recipes.html .

+12
source share

Just use a mixer:

 from mixer.backend.django import mixer # Generate toppings randomly pizza = mixer.blend(Pizza, toppings=mixer.RANDOM) # Set toppings toppings = mixer.cycle(3).blend(Topping) pizza = mixer.blend(Pizza, toppings=toppings) # Generate toppings with name=tomato pizze = mixer.blend(Pizza, toppings__name='tomato') 

Easy, customizable, fast, schematic, declarative, and in some tests you get what you want.

+3
source share

All Articles