Почему pytest выдает ошибку «AttributeError: объект NoneType» не имеет атрибута «_meta» при тестировании создания модели? - PullRequest
0 голосов
/ 27 апреля 2020

Я использую Django 2 и пытаюсь написать несколько юнит-тестов для своих моделей. У меня есть эти модели ...

class CoopTypeManager(models.Manager):

    def get_by_natural_key(self, name):
        return self.get_or_create(name=name)[0]


class CoopType(models.Model):
    name = models.CharField(max_length=200, null=False, unique=True)

    objects = CoopTypeManager()


class CoopManager(models.Manager):
    # Look up by coop type
    def get_by_type(self, type):
        qset = Coop.objects.filter(type__name=type,
                                   enabled=True)
        return qset

class Coop(models.Model):
    objects = CoopManager()
    name = models.CharField(max_length=250, null=False)
    types = models.ManyToManyField(CoopType)
    address = AddressField(on_delete=models.CASCADE)
    enabled = models.BooleanField(default=True, null=False)
    phone = PhoneNumberField(null=True)
    email = models.EmailField(null=True)
    web_site = models.TextField()

Я создал следующую фабрику для автоматической генерации этих моделей ...

import factory
from .models import CoopType, Coop


class CoopTypeFactory(factory.DjangoModelFactory):
    """
        Define Coop Type Factory
    """
    class Meta:
        model = CoopType


class CoopFactory(factory.DjangoModelFactory):
    """
        Define Coop Factory
    """
    class Meta:
        model = Coop

    coop_type = factory.SubFactory(CoopTypeFactory)

Затем я создал этот простой тест ...

import pytest
from django.test import TestCase
from .tests.factories import CoopTypeFactory, CoopFactory


class ModelTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        print("setUpTestData: Run once to set up non-modified data for all class methods.")
        pass

    def setUp(self):
        print("setUp: Run once for every test method to setup clean data.")
        pass

    @pytest.mark.django_db
    def test_coop_type_model():
        """ Test coop type model """
        # create coop type model instance
        coop_type = CoopTypeFactory(name="Test Coop Type Name")
        assert coop_type.name == "Test Coop Type Name"

но когда я запускаю тесты, я получаю сообщение об ошибке: «AttributeError: у объекта« NoneType »нет атрибута« _meta »»

(venv) localhost:web davea$ python manage.py test
Creating test database for alias 'default'...
Got an error creating the test database: (1007, "Can't create database 'test_maps_data'; database exists")
Type 'yes' if you would like to try deleting the test database 'test_maps_data', or 'no' to cancel: yes
Destroying old test database for alias 'default'...
Traceback (most recent call last):
  File "manage.py", line 21, in <module>
    main()
  File "manage.py", line 17, in main
    execute_from_command_line(sys.argv)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/core/management/__init__.py", line 371, in execute_from_command_line
    utility.execute()
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/core/management/__init__.py", line 365, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/core/management/commands/test.py", line 26, in run_from_argv
    super().run_from_argv(argv)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/core/management/base.py", line 288, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/core/management/base.py", line 335, in execute
    output = self.handle(*args, **options)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/core/management/commands/test.py", line 59, in handle
    failures = test_runner.run_tests(test_labels)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/test/runner.py", line 601, in run_tests
    old_config = self.setup_databases()
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/test/runner.py", line 548, in setup_databases
    self.parallel, **kwargs
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/test/utils.py", line 176, in setup_databases
    serialize=connection.settings_dict.get('TEST', {}).get('SERIALIZE', True),
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/db/backends/base/creation.py", line 76, in create_test_db
    self.connection._test_serialized_contents = self.serialize_db_to_string()
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/db/backends/base/creation.py", line 119, in serialize_db_to_string
    serializers.serialize("json", get_objects(), indent=None, stream=out)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/core/serializers/__init__.py", line 128, in serialize
    s.serialize(queryset, **options)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/core/serializers/base.py", line 80, in serialize
    for count, obj in enumerate(queryset, start=1):
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/db/backends/base/creation.py", line 116, in get_objects
    yield from queryset.iterator()
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/db/models/query.py", line 336, in _iterator
    yield from self._iterable_class(self, chunked_fetch=use_chunked_fetch, chunk_size=chunk_size)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/db/models/query.py", line 54, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 1050, in execute_sql
    sql, params = self.as_sql()
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 445, in as_sql
    extra_select, order_by, group_by = self.pre_sql_setup()
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 50, in pre_sql_setup
    self.setup_query()
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 40, in setup_query
    self.query.get_initial_alias()
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/db/models/sql/query.py", line 886, in get_initial_alias
    alias = self.join(BaseTable(self.get_meta().db_table, None))
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/db/models/sql/query.py", line 284, in get_meta
    return self.model._meta
AttributeError: 'NoneType' object has no attribute '_meta'

Редактировать: Вот ссылка на github, если вы хотите попытаться воспроизвести проблему. Проект в "сети" - https://github.com/chicommons/maps

Ответы [ 2 ]

0 голосов
/ 01 мая 2020

Хорошо, я посмотрел ваш код на github, и мое первое предупреждение: не подключайте модели других пакетов, подкласс, если можете .

Ваше использование django -адрес немного сумбурный, вы используете модели и изменяете их поведение, но это довольно опасно.

Уличающие строки

setattr(State._meta, 'default_manager', StateCustomManager())
setattr(Locality._meta, 'default_manager', LocalityCustomManager())

Эти атрибуты являются частными (поэтому _ с префиксом _meta и не следует путать с ним. Я бы использовал State.add_to_class('objects', StateCustomManager()).

Второе предупреждение: я не думаю, что вы используете get_by_natural_key() и автоматическое создание объектов довольно мудро, это может замедлить доступ ваших объектов.

0 голосов
/ 01 мая 2020

Простое решение: игнорировать миграции во время тестового прогона . Для этого создайте отдельный модуль настроек для тестовой среды

# /web/maps/test_settings.py
from .settings import *


class DisableMigrations(object):
    # ref: https://stackoverflow.com/a/28560805/12578202

    def __contains__(self, item):
        return True

    def __getitem__(self, item):
        return None


MIGRATION_MODULES = DisableMigrations()

Затем выполните тест как,

python manage.py test <b>--settings=maps.test_settings</b>

ПРИМЕЧАНИЕ: Я внес другие небольшие изменения в хранилище, чтобы это сделать, но я надеюсь, что эти вещи не имеют большого значения. (Во всяком случае, я сделал для вас пиар )

...