Skip to content

SelfAttribute does not work on a SubFactory field that is a DictFactory #1134

@ryancausey

Description

@ryancausey

Description

When using factory.SelfAttribute to set the value of a field to the value of a nested attribute from a factory.SubFactory field that is a factory.DictFactory, the evaluation of SelfAttribute fails with a AttributeError:

AttributeError: 'dict' object has no attribute 'last_name'

To Reproduce

Run the code below

Model / Factory code
import factory

class ChildFactory(factory.DictFactory):

    first_name = factory.Faker("first_name")
    last_name = factory.Faker("last_name")


class ParentFactory(factory.DictFactory):

    child = factory.SubFactory(ChildFactory)
    last_name = factory.SelfAttribute("child.last_name")

parent = ParentFactory()
The issue

This code sets up the ParentFactory.last_name to be a factory.SelfAttribute of ParentFactory.child.last_name. This succeeds if a regular python class is used as the model instead of a dict.

Python 3.13.3 (main, May 21 2025, 10:47:46) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import factory
...
... class ChildFactory(factory.DictFactory):
...
...     first_name = factory.Faker("first_name")
...     last_name = factory.Faker("last_name")
...
...
... class ParentFactory(factory.DictFactory):
...
...     child = factory.SubFactory(ChildFactory)
...     last_name = factory.SelfAttribute("child.last_name")
...
... parent = ParentFactory()
...
Traceback (most recent call last):
  File "<python-input-0>", line 14, in <module>
    parent = ParentFactory()
  File "/home/rccausey/.cache/pypoetry/virtualenvs/billing-api--M5yUIBR-py3.13/lib/python3.13/site-packages/factory/base.py", line 43, in __call__
    return cls.create(**kwargs)
           ~~~~~~~~~~^^^^^^^^^^
  File "/home/rccausey/.cache/pypoetry/virtualenvs/billing-api--M5yUIBR-py3.13/lib/python3.13/site-packages/factory/base.py", line 539, in create
    return cls._generate(enums.CREATE_STRATEGY, kwargs)
           ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/rccausey/.cache/pypoetry/virtualenvs/billing-api--M5yUIBR-py3.13/lib/python3.13/site-packages/factory/base.py", line 468, in _generate
    return step.build()
           ~~~~~~~~~~^^
  File "/home/rccausey/.cache/pypoetry/virtualenvs/billing-api--M5yUIBR-py3.13/lib/python3.13/site-packages/factory/builder.py", line 270, in build
    step.resolve(pre)
    ~~~~~~~~~~~~^^^^^
  File "/home/rccausey/.cache/pypoetry/virtualenvs/billing-api--M5yUIBR-py3.13/lib/python3.13/site-packages/factory/builder.py", line 211, in resolve
    self.attributes[field_name] = getattr(self.stub, field_name)
                                  ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/rccausey/.cache/pypoetry/virtualenvs/billing-api--M5yUIBR-py3.13/lib/python3.13/site-packages/factory/builder.py", line 356, in __getattr__
    value = value.evaluate_pre(
        instance=self,
        step=self.__step,
        overrides=declaration.context,
    )
  File "/home/rccausey/.cache/pypoetry/virtualenvs/billing-api--M5yUIBR-py3.13/lib/python3.13/site-packages/factory/declarations.py", line 67, in evaluate_pre
    return self.evaluate(instance, step, context)
           ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/rccausey/.cache/pypoetry/virtualenvs/billing-api--M5yUIBR-py3.13/lib/python3.13/site-packages/factory/declarations.py", line 228, in evaluate
    return deepgetattr(target, self.attribute_name, self.default)
  File "/home/rccausey/.cache/pypoetry/virtualenvs/billing-api--M5yUIBR-py3.13/lib/python3.13/site-packages/factory/declarations.py", line 188, in deepgetattr
    return deepgetattr(getattr(obj, attr), subname, default)
  File "/home/rccausey/.cache/pypoetry/virtualenvs/billing-api--M5yUIBR-py3.13/lib/python3.13/site-packages/factory/declarations.py", line 190, in deepgetattr
    return getattr(obj, name)
AttributeError: 'dict' object has no attribute 'last_name'

Notes

Direct self attribute access on the model being created works fine. For example:

>>> class ExampleFactory(factory.DictFactory):
...
...     foo = "foo"
...     bar = factory.SelfAttribute("foo")
...
>>> ExampleFactory()
{'foo': 'foo', 'bar': 'foo'}

The error only appears when attempting to get a nested attribute from an attribute that is a factory.SubFactory of a factory.DictFactory. My suspicion is that the deepgetattr code is not set up to handle the case where a nested object field is a dictionary.

Seen in FactoryBoy version 3.3.3

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions