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

Вернуться на верх