Recursive error in self-referencing SQLAlchemy model

19 views
Skip to first unread message

David Felce

unread,
Nov 7, 2019, 10:32:23 AM11/7/19
to facto...@googlegroups.com, Ed Garabito
Hi, I realise a similar question has been posed before (https://github.com/FactoryBoy/factory_boy/issues/173) but I have tried all the suggestions in that ticket and am still having issues creating a self-referencing field in a SQLAlchemy model. 

Specifically, when I try creating a ScaleFactory object in a test using this format (as suggested in the ticket):

new_scale = factories.ScaleFactory(code=test_code, description_student=test_description_student, parent_scale__parent_scale=None)

I get the following error:

obj = None, name = 'id', default = <class 'factory.declarations._UNSPECIFIED'>

    def deepgetattr(obj, name, default=_UNSPECIFIED):
        """Try to retrieve the given attribute of an object, digging on '.'.
   
        This is an extended getattr, digging deeper if '.' is found.
   
        Args:
            obj (object): the object of which an attribute should be read
            name (str): the name of an attribute to look up.
            default (object): the default value to use if the attribute wasn't found
   
        Returns:
            the attribute pointed to by 'name', splitting on '.'.
   
        Raises:
            AttributeError: if obj has no 'name' attribute.
        """
        try:
            if '.' in name:
                attr, subname = name.split('.', 1)
                return deepgetattr(getattr(obj, attr), subname, default)
            else:
>               return getattr(obj, name)
E               AttributeError: 'NoneType' object has no attribute 'id'


My model is this:
class ScaleFactory(factory.alchemy.SQLAlchemyModelFactory):
    class Meta:
        model = models.Scale
        sqlalchemy_session = models.orm.session

    id = lazy_attribute(lambda o: fake.uuid4())
    code = lazy_attribute(lambda o: fake.name())
    scale_type = lazy_attribute(lambda o: fake.enum(ScaleType))
    description_student = lazy_attribute(lambda o: fake.text())
    description_institution = lazy_attribute(lambda o: fake.text())
    icon = lazy_attribute(lambda o: fake.name())
    hint_student = lazy_attribute(lambda o: fake.text())
    order = factory.Sequence(lambda n: n)
    hint_institution = lazy_attribute(lambda o: fake.text())
    enabled = lazy_attribute(lambda o: fake.boolean())
    parent_scale_id = factory.SelfAttribute("parent_scale.id")
    parent_scale = factory.SubFactory('application.factories.ScaleFactory')

I've also tried all the other suggestions on that support ticket (@factory.post_generation etc) but all result in an infinite recursion. The only call that does not produce an infinite recursion is that one I've given you here, but that results in the AttributeError you see. It's very possible I'm just not understanding what I'm supposed to pass here instead of None as parent_scale__parent_scale. I've tried creating a MagicMock obj as well but that produced an AttributeError trying to find __name__. So I'm totally stuck! Any help is hugely appreciated.

I'm on factory-boy==2.12.0


Best Regards,
David Felce

Reply all
Reply to author
Forward
0 new messages