Why does pytest fail to resolve Related model references in a Django package?
I have an installable Django package that I have built and was starting to write tests for it. I am using pytest-django. However, when I run the tests, almost all the tests fail and I keep getting this error:-
request = <SubRequest 'django_db_setup' for <Function test_filter_with_full_name>>, django_test_environment = None
django_db_blocker = <pytest_django.plugin.DjangoDbBlocker object at 0x10072ba40>, django_db_use_migrations = False, django_db_keepdb = True
django_db_createdb = False, django_db_modify_db_settings = None
@pytest.fixture(scope="session")
def django_db_setup(
request: pytest.FixtureRequest,
django_test_environment: None,
django_db_blocker: DjangoDbBlocker,
django_db_use_migrations: bool,
django_db_keepdb: bool,
django_db_createdb: bool,
django_db_modify_db_settings: None,
) -> Generator[None, None, None]:
"""Top level fixture to ensure test databases are available"""
from django.test.utils import setup_databases, teardown_databases
setup_databases_args = {}
if not django_db_use_migrations:
_disable_migrations()
if django_db_keepdb and not django_db_createdb:
setup_databases_args["keepdb"] = True
aliases, serialized_aliases = _get_databases_for_setup(request.session.items)
with django_db_blocker.unblock():
> db_cfg = setup_databases(
verbosity=request.config.option.verbose,
interactive=False,
aliases=aliases,
serialized_aliases=serialized_aliases,
**setup_databases_args,
)
.venv/lib/python3.12/site-packages/pytest_django/fixtures.py:198:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv/lib/python3.12/site-packages/django/test/utils.py:204: in setup_databases
connection.creation.create_test_db(
.venv/lib/python3.12/site-packages/django/db/backends/base/creation.py:78: in create_test_db
call_command(
.venv/lib/python3.12/site-packages/django/core/management/__init__.py:194: in call_command
return command.execute(*args, **defaults)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.12/site-packages/django/core/management/base.py:460: in execute
output = self.handle(*args, **options)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.12/site-packages/pytest_django/fixtures.py:356: in handle
return super().handle(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.12/site-packages/django/core/management/base.py:107: in wrapper
res = handle_func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.12/site-packages/django/core/management/commands/migrate.py:318: in handle
self.sync_apps(connection, executor.loader.unmigrated_apps)
.venv/lib/python3.12/site-packages/django/core/management/commands/migrate.py:480: in sync_apps
editor.create_model(model)
.venv/lib/python3.12/site-packages/pgtrigger/migrations.py:499: in create_model
super().create_model(model)
.venv/lib/python3.12/site-packages/django/db/backends/base/schema.py:509: in create_model
sql, params = self.table_sql(model)
^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.12/site-packages/django/db/backends/base/schema.py:221: in table_sql
definition, extra_params = self.column_sql(model, field)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.12/site-packages/django/db/backends/base/schema.py:383: in column_sql
field_db_params = field.db_parameters(connection=self.connection)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.12/site-packages/django/db/models/fields/related.py:1230: in db_parameters
target_db_parameters = self.target_field.db_parameters(connection)
^^^^^^^^^^^^^^^^^
.venv/lib/python3.12/site-packages/django/db/models/fields/related.py:1120: in target_field
return self.foreign_related_fields[0]
^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.12/site-packages/django/utils/functional.py:47: in __get__
res = instance.__dict__[self.name] = self.func(instance)
^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.12/site-packages/django/db/models/fields/related.py:788: in foreign_related_fields
rhs_field for lhs_field, rhs_field in self.related_fields if rhs_field
^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.12/site-packages/django/utils/functional.py:47: in __get__
res = instance.__dict__[self.name] = self.func(instance)
^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.12/site-packages/django/db/models/fields/related.py:775: in related_fields
return self.resolve_related_fields()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.12/site-packages/django/db/models/fields/related.py:1147: in resolve_related_fields
related_fields = super().resolve_related_fields()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <django.db.models.fields.related.ForeignKey: organization>
def resolve_related_fields(self):
if not self.from_fields or len(self.from_fields) != len(self.to_fields):
raise ValueError(
"Foreign Object from and to fields must be the same non-zero length"
)
if isinstance(self.remote_field.model, str):
> raise ValueError(
"Related model %r cannot be resolved" % self.remote_field.model
)
E ValueError: Related model 'accounts.Organization' cannot be resolved
.venv/lib/python3.12/site-packages/django/db/models/fields/related.py:755: ValueError
================================================================= short test summary info =================================================================
ERROR tests/filters/test_account_filters.py::TestNameAnnotationFilter::test_filter_with_full_name - ValueError: Related model 'accounts.Organization' cannot be resolved
For reference, this is my conftest.py file:-
# SPDX-FileCopyrightText: © 2024 BeyondIRR <https://beyondirr.com/>
# SPDX-License-Identifier: LicenseRef-beyondirr-proprietary
# Confidential and proprietary code
# Contact: <https://beyondirr.com/contact-us/> | <connect@beyondirr.tech>
import os
import pytest
import django
from django.conf import settings
from dotenv import load_dotenv
def pytest_configure():
if not settings.configured:
load_dotenv(override=True)
settings.configure(
DEBUG_PROPAGATE_EXCEPTIONS=True,
DATABASES={
"default": {
"ENGINE": "django.db.backends.postgresql",
"HOST": os.getenv("INVESTEDGE_DB_HOST", "localhost"),
"NAME": os.getenv("INVESTEDGE_DB_NAME", "testedge"),
"USER": os.getenv("INVESTEDGE_DB_USER", "postgres"),
"PASSWORD": os.getenv("INVESTEDGE_DB_PASSWORD", "postgres"),
"PORT": os.getenv("INVESTEDGE_DB_PORT", "5432"),
}
},
SECRET_KEY="not very secret in tests",
USE_I18N=True,
STATIC_URL="/static/",
ROOT_URLCONF="tests.urls",
TEMPLATES=[
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"APP_DIRS": True,
},
],
MIDDLEWARE=[
"django.middleware.common.CommonMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
],
INSTALLED_APPS=[
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.staticfiles",
"pgtrigger",
"tests",
"investedge",
"investedge.instant_review",
],
PASSWORD_HASHERS=("django.contrib.auth.hashers.MD5PasswordHasher",),
ORGANIZATION_MODEL="tests.Organization",
AUTH_USER_MODEL="tests.Account",
SITE_ID=1,
DEFAULT_AUTO_FIELD="django.db.models.AutoField",
)
django.setup()
@pytest.fixture
def make_organization():
def _make_organization(name="Test Organization", **kwargs):
from tests.models import Organization
return Organization.objects.create(name=name, **kwargs)
return _make_organization
@pytest.fixture
def make_user(make_organization, django_user_model):
def _make_user(email, organization=None, role="C", **kwargs):
from django.utils.crypto import get_random_string
return django_user_model.objects.create(
email=email,
role=role,
organization=organization or make_organization(),
username=f"{role}{get_random_string(9).upper()}",
first_name=kwargs.get("first_name", "Test"),
last_name=kwargs.get("last_name", "User"),
permissions=kwargs.get("permissions", 0),
contact="1234567890",
)
return _make_user
@pytest.fixture
def make_family(make_organization):
def _make_family(name="Test Family", organization=None):
from investedge.accounts.models import FamilyInfo
if not organization:
organization = make_organization()
return FamilyInfo.objects.create(name=name, organization=organization)
return _make_family
@pytest.fixture
def make_client(make_user, make_organization):
def _make_client(user=None, family=None, manager=None, is_restricted=False, is_instant_client=False,**kwargs):
from random import choices as r
from string import ascii_uppercase as ASCII
from string import digits
from investedge.accounts.models import ClientInfo
return ClientInfo.objects.create(
user=user or make_user(**kwargs),
is_restricted=is_restricted,
is_instant_client = is_instant_client,
manager=manager or make_user(email="test@manager.com"),
organization=make_organization(),
family=family,
pan_no="".join(r(ASCII, k=5) + r(digits, k=2) + r(ASCII)),
)
return _make_client
def request_factory():
from rest_framework.test import APIRequestFactory
return APIRequestFactory()
And this is a single test file I have written:-
# SPDX-FileCopyrightText: © 2024 BeyondIRR <https://beyondirr.com/>
# SPDX-License-Identifier: LicenseRef-beyondirr-proprietary
# Confidential and proprietary code
# Contact: <https://beyondirr.com/contact-us/> | <connect@beyondirr.tech>
import pytest
from investedge.accounts.filters import NameAnnotationFilter
from tests.models import Account
@pytest.mark.django_db
class TestNameAnnotationFilter:
def test_filter_with_full_name(self,make_user):
first_user = make_user(email="JohnDoe@gmail.com",first_name="John",last_name="Doe")
second_user = make_user(email="NotJohnDoe@gmail.com",first_name="Not",last_name="JD")
queryset = Account.objects.all()
filtered_queryset = NameAnnotationFilter(queryset=queryset,data={"name":"John"}).qs
assert first_user in filtered_queryset
assert second_user not in filtered_queryset
Any help with this would be appreciated