I have a model, that has a post_generation hook.
I want to pass parameter to this post_generation hook from another factory, as SubFactory parameter to be a SelfAttribute, so these 2 models will be logically synced.
It might be that I'm doing something wrong, so advice is highly appreciated.
To Reproduce
When I create OrderItemFactory() it throws AttributeError: The parameter 'size' is unknown...
Evaluated attributes are in fact from SubFactory's factory (product), not from top level factory (order item).
Model / Factory code
Here is simplified version
import factory
from factory import fuzzy
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
SIZES = ('s', 'm', 'l')
# Models
class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String())
price = db.Column(db.Float(asdecimal=True), nullable=False)
class Inventory(db.Model):
id = db.Column(db.Integer(), primary_key=True)
size = db.Column(db.String(), nullable=True)
quantity = db.Column(db.Integer())
product_id = db.Column(db.Integer(), db.ForeignKey('product.id'), nullable=False)
product = db.relationship(Product, backref=db.backref('inventory', lazy=True))
class OrderItem(db.Model):
id = db.Column(db.Integer(), primary_key=True)
size = db.Column(db.String(), nullable=True)
quantity = db.Column(db.Integer(), nullable=False)
product_id = db.Column(db.Integer(),
db.ForeignKey('product.id'),
nullable=False)
product = db.relationship('Product')
# Factories
class BaseFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
abstract = True
sqlalchemy_session = db.session
sqlalchemy_session_persistence = 'flush'
class InventoryFactory(base_factory):
class Meta:
model = Inventory
size = factory.fuzzy.FuzzyChoice(SIZES)
quantity = factory.fuzzy.FuzzyInteger(1, 100)
class ProductFactory(BaseFactory):
class Meta:
model = Product
name = factory.Sequence(lambda n: 'Product %d' % n)
price = fuzzy.FuzzyDecimal(300, 4000)
@factory.post_generation
def add_inventory(self, create, how_many, **kwargs):
for n in range(how_many):
size = kwargs.get('size', set(SIZES).pop())
InventoryFactory(product=self, size=size)
class OrderItemFactory(BaseFactory):
class Meta:
model = OrderItem
size = factory.fuzzy.FuzzyChoice(SIZES)
quantity = factory.fuzzy.FuzzyInteger(1, 10)
product = factory.SubFactory(
ProductFactory,
# This works
name=factory.SelfAttribute('..size'),
add_inventory=1,
# This works:
# add_inventory__size='l',
add_inventory__size=factory.SelfAttribute('..size'),
)
The issue
Add a short description along with your code
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/base.py:39: in __call__
return cls.create(**kwargs)
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/base.py:527: in create
return cls._generate(enums.CREATE_STRATEGY, kwargs)
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/alchemy.py:51: in _generate
return super(SQLAlchemyModelFactory, cls)._generate(strategy, params)
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/base.py:464: in _generate
return step.build()
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/builder.py:276: in build
step.resolve(pre)
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/builder.py:217: in resolve
self.attributes[field_name] = getattr(self.stub, field_name)
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/builder.py:382: in __getattr__
extra=context,
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/declarations.py:315: in evaluate
return self.generate(step, defaults)
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/declarations.py:440: in generate
return step.recurse(subfactory, params, force_sequence=force_sequence)
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/builder.py:234: in recurse
return builder.build(parent_step=self, force_sequence=force_sequence)
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/builder.py:292: in build
context=declaration.context,
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/declarations.py:35: in unroll_context
return step.recurse(subfactory, context, force_sequence=step.sequence)
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/builder.py:234: in recurse
return builder.build(parent_step=self, force_sequence=force_sequence)
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/builder.py:276: in build
step.resolve(pre)
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/builder.py:217: in resolve
self.attributes[field_name] = getattr(self.stub, field_name)
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/builder.py:382: in __getattr__
extra=context,
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/declarations.py:153: in evaluate
return deepgetattr(target, self.attribute_name, self.default)
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/declarations.py:115: in deepgetattr
return getattr(obj, name)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <Resolver for <BuildStep for <StepBuilder(<SQLAlchemyOptions for ProductFactory>, strategy='create')>>>
name = 'size'
def __getattr__(self, name):
"""Retrieve an attribute's value.
This will compute it if needed, unless it is already on the list of
attributes being computed.
"""
if name in self.__pending:
raise errors.CyclicDefinitionError(
"Cyclic lazy attribute definition for %r; cycle found in %r." %
(name, self.__pending))
elif name in self.__values:
return self.__values[name]
elif name in self.__declarations:
declaration = self.__declarations[name]
value = declaration.declaration
if enums.get_builder_phase(value) == enums.BuilderPhase.ATTRIBUTE_RESOLUTION:
self.__pending.append(name)
try:
context = value.unroll_context(
instance=self,
step=self.__step,
context=declaration.context,
)
value = value.evaluate(
instance=self,
step=self.__step,
extra=context,
)
finally:
last = self.__pending.pop()
assert name == last
self.__values[name] = value
return value
else:
raise AttributeError(
"The parameter %r is unknown. Evaluated attributes are %r, "
> "definitions are %r." % (name, self.__values, self.__declarations))
E AttributeError: The parameter 'size' is unknown. Evaluated attributes are {'name': 'l', 'price': Decimal('3632.62')}, definitions are <DeclarationSet: {'name': <SelfAttribute('size', default=<class 'factory.declarations._UNSPECIFIED'>)>, 'price': <factory.fuzzy.FuzzyDecimal object at 0x11251eeb8>}>.
../../../.local/share/virtualenvs/merch-x5SnXW63/lib/python3.7/site-packages/factory/builder.py:393: AttributeError
Description
I have a model, that has a post_generation hook. I want to pass parameter to this post_generation hook from another factory, as SubFactory parameter to be a SelfAttribute, so these 2 models will be logically synced.
It might be that I'm doing something wrong, so advice is highly appreciated.
To Reproduce
When I create
OrderItemFactory()
it throwsAttributeError: The parameter 'size' is unknown...
Evaluated attributes are in fact from SubFactory's factory (product), not from top level factory (order item).Model / Factory code
Here is simplified version
The issue
Add a short description along with your code
Notes
Factory boy version 3.1.0