Open jcquarto opened 8 years ago
You should be using EventFactory.build()
.
When you call factory.build(dict, FACTORY_CLASS=EventFactory)
twice, this is equivalent to doing:
class E0Factory(EventFactory):
class Meta:
model = dict
e0 = E0Factory.build()
class E1Factory(EventFactory):
class Meta:
model = dict
e1 = E1Factory.build()
Thus, both instances are actually built from different factory classes, each having its sequence starting back to zero.
Then I guess I'm having confusion understanding how to use both the MongoEngine section of https://factoryboy.readthedocs.org/en/latest/orms.html combined with the converting of a factory's output to a dict of http://factoryboy.readthedocs.org/en/latest/recipes.html
If I follow precisely what you wrote, then my code (from above) looks like: testcases_spec.py:
it('should correctly generate sequences via Factory'):
e0 = EventFactory.build()
e1 = EventFactory.build()
which results in: Failure/Error: spec/pychronicle_spec.py e0 = EventFactory.build() KeyError: '_cls'
(sounds like an error related to setting the Sequence?)
SO I understand when you describe the WHY behind different factory classes, each having its own sequence. But I'm not understanding HOW you're intending we should implement this to both build, and then later, convert to a dict
Continuing to try to isolate the problem. The following works, as expected and as documented:
class Cat:
def __init__(self, eyes='green', toys=0):
self._eyes = eyes
self._toys = toys
@property
def eyes(self):
return self._eyes
@eyes.setter
def eyes(self, e):
self._eyes = e
@property
def toys(self):
return self._toys
@toys.setter
def toys(self, t):
self._toys = t
class CatFactory(factory.Factory):
class Meta:
model = Cat
eyes = "blue"
toys = factory.Sequence(lambda n: n)
thus:
from factories import *
cato = CatFactory()
print cato.eyes, cato.toys
gives
blue 0
and
cato_green = CatFactory(eyes = 'green')
print cato_green.eyes, cato_green.toys
gives
green 1
but when I use the format the docs suggest for working with ORM , it does not:
class Event(mongoengine.Document):
name = mongoengine.StringField(required=True)
event_id = mongoengine.StringField(required=True)
class EventFactory(factory.mongoengine.MongoEngineFactory):
class Meta:
model = Event
name = factory.Sequence(lambda n: 'event_%d' % n)
event_id = factory.Faker('uuid4')
and this:
event_0 = EventFactory()
print event_0.name
yields the error:
KeyError: '_cls'
I'm reopening, since @jcquarto is actively looking into this. I don't use the MongoEngine stuff myself, so unclear if this is a code bug, a docs bug, or works-as-intended. I suspect a docs bug myself, as the recipe for converting to a dict only recently got added.
@jcquarto - if you figure out what's going on, we'd really appreciate if you could submit a PR.
I'm currently at a loss. The answer that @rbarrois gave looked correct as per the docs, but doesn't work when I code it that way. I was hoping the error message mentioned above ("KeyError: '_cls' ") would spark some insight .
When I use the other technique from the documentation:
e1 = factory.build(dict, FACTORY_CLASS=EventFactory)
it at least the constructor works, although Sequence doesn't sequence
when I don't use mongoengine
, the factory works as expected. however, dict
does not
My solution is not to use mongoengine anymore, and stead use a snippet I found on StackOverflow for converting an object instance to a dict:
import inspect
def props(obj):
pr = {}
for name in dir(obj):
value = getattr(obj, name)
if not name.startswith('__') and not inspect.ismethod(value):
pr[name] = value
return pr
thus now I do:
e0 = EventFactory.build()
e1 = EventFactory.build()
which gets me the build() as per @rbarrois and then I do
q0 = props(e0)
q1 = props(e1)
to convert them into dicts. This bypasses the not-working-with-mongoengine approach of
q0 = factory.build(dict, FACTORY_CLASS=EventFactory)
q1 = factory.build(dict, FACTORY_CLASS=EventFactory)
I hope that's a useful enough of a hint that any readers can either use it or track down where in the code dict
doesn't play nice with mongoengine
@jcquarto in your stack trace, where does the error appear (with _cls
)?
This could be a bug in factory_boy, so I'd like to fix it.
If you have a working factory for MongoEngine and want to extract it as a dict, you may also do the following:
class EventFactory(factory.mongoengine.MongoEngineFactory):
# Your code here
class EventAsDictFactory(EventFactory):
class Meta:
model = dict
EventAsDictFactory.build()
here's the stack trace from the last time I ran the tests before I scooted around the problem by by-passing the use of Sequence with mongoengine.
1) pychronicle: CRUD behavior: it should correctly generate sequences via Factory
Failure/Error: spec/pychronicle_spec.py e0 = BaseEventFactory()
KeyError: '_cls'
File "/Users/john/anaconda/lib/python2.7/site-packages/mongoengine/fields.py", line 591, in to_python
doc_cls = get_document(value['_cls'])
File "/Users/john/anaconda/lib/python2.7/site-packages/mongoengine/base/document.py", line 118, in __init__
value = field.to_python(value)
File "/Users/john/anaconda/lib/python2.7/site-packages/factory/mongoengine.py", line 45, in _create
instance = model_class(*args, **kwargs)
File "/Users/john/anaconda/lib/python2.7/site-packages/factory/base.py", line 467, in _prepare
return cls._create(model_class, *args, **kwargs)
File "/Users/john/anaconda/lib/python2.7/site-packages/factory/base.py", line 492, in _generate
obj = cls._prepare(create, **attrs)
File "/Users/john/anaconda/lib/python2.7/site-packages/factory/base.py", line 567, in create
return cls._generate(True, attrs)
File "/Users/john/anaconda/lib/python2.7/site-packages/factory/base.py", line 81, in __call__
return cls.create(**kwargs)
File "spec/pychronicle_spec.py", line 105, in 00000007__it should correctly generate sequences via Factory
e0 = BaseEventFactory()
the only difference from the very top of this thread is that the last thing I tried was to re-name the class name from Event
to BaseEvent
(ditto with the factory) in the nominal hope that "Event" was some sort of reserved word.
Is there a way to understand what conditions would cause Sequence not to work as expected? I tried this in a test: factories.py
testcases_spec.py:
the first 'should' passes (name is 'event_0', as expected), yet the second one fails (name is 'event_0', rather than the expected 'event_1')
any pointers? (python 2.7, factory_boy 2.6.x, uptodate MongoDB, though as you can see from the above that's not even touched in the test.