Help me understand: factory.errors.CyclicDefinitionError: Cyclic lazy attribute definition for
See original GitHub issueIn the code below, I can create a NotificationEvent, but I cannot create a Notification, if I also try to create the NotificationEvent.
I’ve tried several different things, but I’m finally stuck with this error:
factory.errors.CyclicDefinitionError: Cyclic lazy attribute definition for user_account; cycle found in ['account', 'user_account'].
Sadly there is not much in the way of docs to help solve what is going on here. I’m going to try using Params
and see if that helps, but I think it does mostly the same as what I have below.
Is it possible to get some more documentation around this error and around how you create Objects that have some nesting, but can be used without the nesting?
# encoding: utf-8
import factory
from .uniregistry_model_factory_base import UniregistryModelFactoryBase
from uniregistrar.models.db import DBSession
from uniregistrar.factories import UserAccountFactory
from notification.models import (Notification, NotificationEvent, NotificationSubcategory,
NotificationSeverity)
class NotificationEventFactory(UniregistryModelFactoryBase):
class Meta:
model = NotificationEvent
sqlalchemy_session = DBSession
exclude = ("user_account", )
user_account = factory.SubFactory(UserAccountFactory)
subcategory = "domain-sold"
severity = "info"
account = factory.SelfAttribute("user_account.account")
message = factory.Sequence(lambda n: "Message %d" % n)
object = factory.Sequence(lambda n: "Object %d" % n)
url = factory.Sequence(lambda n: "https://uni.com/url?p=%d" % n)
@classmethod
def attributes(cls, create=False, extra=None):
if 'account' in extra:
raise ValueError("Pass a user_account, not individual account values")
return super(NotificationEventFactory, cls).attributes(create, extra)
@classmethod
def _create(cls, model_class, *args, **kwargs):
return super(NotificationEventFactory, cls)._create(model_class, *args, **kwargs)
class NotificationFactory(UniregistryModelFactoryBase):
class Meta:
model = Notification
sqlalchemy_session = DBSession
exclude = ("user_account", )
user_account = factory.SubFactory(UserAccountFactory)
user = factory.SelfAttribute("user_account.user")
event = factory.SubFactory(NotificationEventFactory,
user_account=factory.SelfAttribute("user_account"))
# website_show = defaults to True in the DB
# viewed_date = timestamp, defaults to NULL in the DB
# notified_date = timestamp defaults to NULL in the DB
@classmethod
def attributes(cls, create=False, extra=None):
if 'user' in extra:
raise ValueError("Pass a user_account, not individual user values")
return super(NotificationFactory, cls).attributes(create, extra)
@classmethod
def _create(cls, model_class, *args, **kwargs):
return super(NotificationFactory, cls)._create(model_class, *args, **kwargs)
Issue Analytics
- State:
- Created 5 years ago
- Reactions:2
- Comments:5 (1 by maintainers)
Top Results From Across the Web
django - How to resolve CyclicDefinitionError in factory_boy ...
I've found solution. ModelBFactory should look like this: #in ModelB_App/factories ...
Read more >How To Resolve Cyclicdefinitionerror In Factoryboy ... - ADocLib
[Read fixes] Steps to fix this factoryboy exception: Full details: errors.CyclicDefinitionError: Cyclic lazy attribute definition for %r; cycle found.
Read more >Reference — Factory Boy stable documentation
This attribute indicates that the Factory subclass should not be used to generate objects, but instead provides some extra defaults. It will be...
Read more >Model Your Models With Factory Boy (Setting Up Chained ...
A tip for improving tests: Subclassed Factories; Final Code Samples; Common Errors. TypeError: "my_attribute" is an invalid keyword argument ...
Read more >Synthesizing Realistic Substitute Data for a Law Enforcement ...
Instead, we should use the safer method, or what is known as the lazy attribute, which will define the attributes each time the...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Hi! Indeed, we should write a « solving common errors » section in the docs — any help would be more than welcome!
Regarding your issue, let’s walk through the messages.
The error
You get an error saying “I find a cyclic definition: the value of
user_account
depends on the value ofaccount
, which itself relies onuser_account
”.Under the hood, factory_boy starts by collecting all declarations from the factory it’s going to instantiate, and walks through all those attributes to “resolve” them (i.e convert a declaration into an actual value). Since some fields might depend on the value of other fields, we keep a list of « attributes currently being computed »: if A depends on B, and B on C, and C on A, we’ll raise when we discover that C is trying to access the value of A. => That’s the issue you see here.
The cause
So, where do we have both
account
depending onuser_account
, anduser_account
onuser_account
?When you call your
NotificationFactory
, it tries to instantiate aNotificationEventFactory
with an extra declaration,user_account=factory.SelfAttribute("user_account")
. This extra declaration is added to the list of declarations of theNotificationEventFactory
during that instantiation.This is equivalent to the following (shortened) factory:
Note how:
user_account=factory.SelfAttribute()
passed in theSubFactory
shadows the initialuser_account = factory.SubFactory()
from the initial class declaration;user_account
takes the value ofuser_account
”. => Your issue is in that second pointThe solution
From your description, when building a
Notification
, you want theNotificationEvent
to use the sameUserAccount
as theNotification
.The proper declaration is thus:
Basically, any extra declarations added in a
SubFactory
are evaluated from the point of view of theSubFactory
; we need to get back to the factory calling ourSubFactory
, thus the..user_account
That last part was what I had figured out and you did indeed confirm what I was suspecting. The lazy part of that declaration is so lazy it doesn’t get evaluated when the SubFactory “call” is made, but when it is actually used within the SubFactory and that is why you need the … at the beginning of the SelfAttribute.
On Mon, Nov 12, 2018 at 5:00 AM Raphaël Barrois notifications@github.com wrote: