factory_boy
factory_boy copied to clipboard
Fields do not exist in this model errors with OneToOneField in Django 5
Description
After upgrading from Django 4.2 to Django 5, some of our tests are failing. These are using a OneToOneField between two models. Creating one instance through a factory with an instance to the other model fails because the related name is not accepted by the Django model manager.
The workaround is very simple (see below), but I think this is a bug in this library as this was working fine under Django 4.2. We're using factory boy 3.3.
To Reproduce
Share how the bug happened:
Model / Factory code
class Shop(models.Model):
pass
class Event(models.Model):
default_shop = models.OneToOneField(
"shop.Shop",
related_name="default_event",
on_delete=models.SET_NULL,
null=True,
blank=True,
)
class EventFactory(factory.django.DjangoModelFactory):
class Meta:
model = Event
skip_postgeneration_save = True
class ShopFactory(factory.django.DjangoModelFactory):
class Meta:
model = Shop
The issue
Before, we were able to first create an Event, and then create a Shop and immediately set the default_event
of the Shop instance to the Event instance. With Django 5, this now fails in the factory, while still working in a Django shell. So it seems like an issue with factory boy not supporting Django 5 properly here.
@pytest.mark.django_db
class Test:
@pytest.fixture
def shop(self):
event = EventFactory.create()
shop = ShopFactory.create(default_event=event)
$ pytest
> shop = ShopFactory.create(default_event=event)
apps/foo/tests/test_foo.py:335:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../.cache/pypoetry/virtualenvs/foo-KcrdI-pR-py3.10/lib/python3.10/site-packages/factory/base.py:528: in create
return cls._generate(enums.CREATE_STRATEGY, kwargs)
../../.cache/pypoetry/virtualenvs/foo-KcrdI-pR-py3.10/lib/python3.10/site-packages/factory/django.py:121: in _generate
return super()._generate(strategy, params)
../../.cache/pypoetry/virtualenvs/foo-KcrdI-pR-py3.10/lib/python3.10/site-packages/factory/base.py:465: in _generate
return step.build()
../../.cache/pypoetry/virtualenvs/foo-KcrdI-pR-py3.10/lib/python3.10/site-packages/factory/builder.py:274: in build
instance = self.factory_meta.instantiate(
../../.cache/pypoetry/virtualenvs/foo-KcrdI-pR-py3.10/lib/python3.10/site-packages/factory/base.py:317: in instantiate
return self.factory._create(model, *args, **kwargs)
../../.cache/pypoetry/virtualenvs/foo-KcrdI-pR-py3.10/lib/python3.10/site-packages/factory/django.py:174: in _create
return manager.create(*args, **kwargs)
../../.cache/pypoetry/virtualenvs/foo-KcrdI-pR-py3.10/lib/python3.10/site-packages/django/db/models/manager.py:87: in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <QuerySet []>
kwargs = {'default_event': <Event: Event>, ...}
reverse_one_to_one_fields = frozenset({'default_event'})
def create(self, **kwargs):
"""
Create a new object with the given kwargs, saving it to the database
and returning the created object.
"""
reverse_one_to_one_fields = frozenset(kwargs).intersection(
self.model._meta._reverse_one_to_one_field_names
)
if reverse_one_to_one_fields:
> raise ValueError(
"The following fields do not exist in this model: %s"
% ", ".join(reverse_one_to_one_fields)
)
E ValueError: The following fields do not exist in this model: default_event
../../.cache/pypoetry/virtualenvs/foo-KcrdI-pR-py3.10/lib/python3.10/site-packages/django/db/models/query.py:670: ValueError
Notes
The workaround is very easy, just assign the relation after the object instance is created:
shop = ShopFactory.create()
shop.default_event = event