django filtering querysets вызывает «AppRegistryNotReady: модели еще не загружены». с формами в models.py

В ходе разработки нашего текущего django-проекта (django 1.7.1) мы столкнулись с задачей пометить некоторые записи базы данных как удаленные и удалить их из поля зрения пользователя, но не полностью из базы данных.

Я нашел быстрое и простое решение django-logicaldelete (именно то, что мы хотим сделать!)

Следуя инструкциям django-logicaldelete, я установил logicaldelete с помощью pip и добавил его как INSTALLED_APP в settings.py.

Осталось только добавить logicaldelete в models.py и admin.py, унаследованный от него.

...
import logicaldelete

class MyModel(logicaldelete.models.Model){...}
... 

...
import logicaldelete
    
class MyModelAdmin(logicaldelete.admin.ModelAdmin){...}
... 

Пока все хорошо :) Попытка запустить проект теперь вызывает «AppRegistryNotReady: модели еще не загружены». - Ошибка:

    Traceback (most recent call last):
      File "C:\Python34\lib\site-packages\django\db\models\options.py", line 414, in get_field_by_name
        return self._name_map[name]
    AttributeError: 'Options' object has no attribute '_name_map'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Python34\lib\site-packages\django\db\models\options.py", line 561, in get_all_related_m2m_objects_with_model
    cache = self._related_many_to_many_cache
AttributeError: 'Options' object has no attribute '_related_many_to_many_cache'

During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "C:\eclipse\workspacePraxis\promotionsdatenbank\manage.py", line 10, in <module>
        execute_from_command_line(sys.argv)
      File "C:\Python34\lib\site-packages\django\core\management\__init__.py", line 385, in execute_from_command_line
        utility.execute()
      File "C:\Python34\lib\site-packages\django\core\management\__init__.py", line 354, in execute
        django.setup()
      File "C:\Python34\lib\site-packages\django\__init__.py", line 21, in setup
        apps.populate(settings.INSTALLED_APPS)
      File "C:\Python34\lib\site-packages\django\apps\registry.py", line 108, in populate
        app_config.import_models(all_models)
      File "C:\Python34\lib\site-packages\django\apps\config.py", line 202, in import_models
        self.models_module = import_module(models_module_name)
      File "C:\Python34\lib\importlib\__init__.py", line 109, in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
      File "<frozen importlib._bootstrap>", line 2254, in _gcd_import
      File "<frozen importlib._bootstrap>", line 2237, in _find_and_load
      File "<frozen importlib._bootstrap>", line 2226, in _find_and_load_unlocked
      File "<frozen importlib._bootstrap>", line 1200, in _load_unlocked
      File "<frozen importlib._bootstrap>", line 1129, in _exec
      File "<frozen importlib._bootstrap>", line 1471, in exec_module
      File "<frozen importlib._bootstrap>", line 321, in _call_with_frames_removed
      File "C:\eclipse\workspacePraxis\promotionsdatenbank\personen\models.py", line 144, in <module>
        class PromovendChoice(forms.Form):
      File "C:\eclipse\workspacePraxis\promotionsdatenbank\personen\models.py", line 149, in PromovendChoice
        promovend_choice = forms.ModelChoiceField(queryset=Promovend.objects.all(), to_field_name='nachname', empty_label="Promovend")
      File "C:\Python34\lib\site-packages\django\db\models\manager.py", line 191, in all
        return self.get_queryset()
      File "C:\Python34\lib\site-packages\logicaldelete\managers.py", line 15, in get_queryset
        return LogicalDeleteQuerySet(self.model, using=self._db).filter(date_removed__isnull=True)
      File "C:\Python34\lib\site-packages\django\db\models\query.py", line 691, in filter
        return self._filter_or_exclude(False, *args, **kwargs)
      File "C:\Python34\lib\site-packages\django\db\models\query.py", line 709, in _filter_or_exclude
        clone.query.add_q(Q(*args, **kwargs))
      File "C:\Python34\lib\site-packages\django\db\models\sql\query.py", line 1287, in add_q
        clause, require_inner = self._add_q(where_part, self.used_aliases)
      File "C:\Python34\lib\site-packages\django\db\models\sql\query.py", line 1314, in _add_q
        current_negated=current_negated, connector=connector)
      File "C:\Python34\lib\site-packages\django\db\models\sql\query.py", line 1138, in build_filter
        lookups, parts, reffed_aggregate = self.solve_lookup_type(arg)
      File "C:\Python34\lib\site-packages\django\db\models\sql\query.py", line 1076, in solve_lookup_type
        _, field, _, lookup_parts = self.names_to_path(lookup_splitted, self.get_meta())
      File "C:\Python34\lib\site-packages\django\db\models\sql\query.py", line 1339, in names_to_path
        field, model, direct, m2m = opts.get_field_by_name(name)
      File "C:\Python34\lib\site-packages\django\db\models\options.py", line 416, in get_field_by_name
        cache = self.init_name_map()
      File "C:\Python34\lib\site-packages\django\db\models\options.py", line 445, in init_name_map
        for f, model in self.get_all_related_m2m_objects_with_model():
      File "C:\Python34\lib\site-packages\django\db\models\options.py", line 563, in get_all_related_m2m_objects_with_model
        cache = self._fill_related_many_to_many_cache()
      File "C:\Python34\lib\site-packages\django\db\models\options.py", line 577, in _fill_related_many_to_many_cache
        for klass in self.apps.get_models():
      File "C:\Python34\lib\functools.py", line 428, in wrapper
        result = user_function(*args, **kwds)
      File "C:\Python34\lib\site-packages\django\apps\registry.py", line 168, in get_models
        self.check_models_ready()
      File "C:\Python34\lib\site-packages\django\apps\registry.py", line 131, in check_models_ready
        raise AppRegistryNotReady("Models aren't loaded yet.")
    django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.

Я читал об этой проблеме с django 1.7 и как решение

import django
django.setup()

дано. Здесь я не работаю, чтобы добавить эти строки. Пытался добавить его в logicaldelete.managers.py и logicaldelete.query.py.

Кажется, у меня неправильное представление о том, что вызывает проблему.

Единственная часть моего собственного кода, показанная в трассировке ошибок, — это вызов в форме:

promovend_choice = forms.ModelChoiceField(queryset=Promovend.objects.all(), to_field_name='nachname', empty_label="Promovend")

Надеюсь, вы знаете, как решить эту проблему...

Если нет, я просто переопределю метод удаления и добавлю менеджера самостоятельно.

Обновление №. 3

Я сам реализовал BaseModel и подходящий менеджер (он почти не отличается от logicaldelete)

class SoftDeleteBaseModel(models.Model):

    deleted = models.BooleanField(default=False)
    
    objects = SoftDeleteManager(False)
    objects_deleted = SoftDeleteManager(True)
    all_objects = models.Manager()
    
    def delete(self):
        related_models = [relation.get_accessor_name() for relation in self._meta.get_all_related_objects()]
        
        for model_objects in rleated_models:
            related_objects = getattr(self, model_objects).all()

            for related_object in related_objects:
                if not issubclass(related_object.__class__, SoftDeleteBaseModel):  
                    break
                related_object.delete()
        
        self.deleted= True
        self.save()
        
    class Meta:
        abstract = True

class SoftDeleteManager(models.Manager):
    
    def __init__(self, show_deleted=False):
        super(SoftDeleteManager, self).__init__()
        self.show_deleted = show_deleted
         
    def get_queryset(self):
        qs = super(SoftDeleteManager, self).get_queryset()
        return qs.filter(deleted=self.show_deleted)

К сожалению, возникает та же ошибка. Импорт django и вызов django.setup() в моем файле manager.py также не работает.

Возникла проблема с вызовом (самодельного) менеджера с помощью

objects = SoftDeleteManager(False)
objects_deleted = SoftDeleteManager(True)
all_objects = models.Manager()

Кажется, что SoftDeleteManger не готов (загружен), пока моя модель хочет сослаться на него.

Обновление № 4

Новые выводы:

Предполагать, что ошибка лежит в моей форме, было не так уж и плохо! Мне интересно, что использование queryset = model.objects.all() со стандартным менеджером работает нормально, замена его на мой SoftDeleteManager вызывает ошибку...

Возникла идея: почему бы не добавить фильтр к этой кодовой строке с помощью стандартного менеджера? queryset = models.objects.all().filter(deleted=False) Этот код также вызывает ошибку AppRegistryNotReady - поэтому основная причина, по-видимому, заключается в фильтрации наборов запросов в моем самодельном менеджере...

Редактировать

Я обнаружил аналогичную проблему с фильтрацией наборов запросов здесь. Но, как упоминалось выше, я безуспешно пытался использовать import django и django.setup()...

Обновление № 5

Прочитав сто раз, вызов django.setup() решит проблему, которую я назвал в своем models.py, ожидая ошибки или успешного запуска... но ничего не происходит. Запуск проекта создает процесс python, но выходных данных нет, и сервер разработки не запускается...

Привет, Тоби


person Tobi    schedule 19.11.2014    source источник


Ответы (2)


Наконец я нашел "скрытое" решение где-то в предыдущем посте (к сожалению, я потерял источник):

В моем models.py раньше был файл django.form. Предположим, что команда форм вызывает ошибку. Я реструктурировал свое приложение и создал для этой формы отдельный файл forms.py. Наконец-то работает отлично!

person Tobi    schedule 21.11.2014
comment
я думаю, вам не нужно вызывать django.setup() в этом файле - person Alvaro; 21.11.2014
comment
Проблема заключалась в использовании кода, который зависит от моделей в файле models.py. django загружает все эти файлы в бутстрап, и они не были готовы по запросу - person Alvaro; 21.11.2014
comment
Твое право! django.setup() не требуется... Мир джанго становится более понятным. Спасибо еще раз. - person Tobi; 21.11.2014

Кажется, это проблема с этой библиотекой. Я предлагаю вам реализовать собственное логическое удаление (что довольно просто с помощью LogicalDeleteManager и LogicalDeleteModel) или использовать более низкую версию django.

В качестве упрощенного примера:

class MyLogicalDeleteManager(models.Manager):
    def __init__(self, show_deleted_objects=False)
        super(MyLogicalDeleteManager, self).__init__()
        self.show_deleted_objects = show_deleted_objects

    def get_queryset(self):
         qs = super(MyLogicalDeleteManager, self).get_queryset()
         return qs.filter(deleted=self.show_deleted_objects)

class MyLogicalDeleteModel(models.Model):
    deleted = models.BooleanField(default=False)
    objects = MyLogicalDeleteManager(False)
    deleted_objects = MyLogicalDeleteManager(True)
    all_objects = models.Manager()

    def delete(self):
        # Maybe check FK constraints with self._meta.get_all_related_objects()
        self.deleted = True
        self.save()
person Alvaro    schedule 19.11.2014
comment
Надеялся пройти этот шаг... он был примерно спланирован аналогично вашему решению - спасибо за пример кода :) - person Tobi; 19.11.2014
comment
Это займет некоторое время, но я обнаружил, что в долгосрочной перспективе проще заставить эти вещи работать именно так, как вы хотите :) - person Alvaro; 19.11.2014
comment
К сожалению, написание моей собственной BaseModel и Manager тоже не работает:/Происходит та же ошибка... Если подумать, моя собственная форма, вызывающая object.all(), дает сбой, но я не знаю, почему - person Tobi; 20.11.2014
comment
Странно... вы недавно обновляли django? ошибка возникает на каждой модели или только на новой? - person Alvaro; 20.11.2014
comment
Ошибка возникает при использовании только формы модели - или, точнее, при вызове (переопределенного) менеджера моделей в этой форме. - person Tobi; 21.11.2014
comment
Новые выводы: предположить, что ошибка находится в моей форме, было не так уж и плохо! Я подумал, что использование queryset = model.objects.all() со стандартным менеджером работает нормально, замена его на мой SoftDeleteManager вызывает ошибку... пришла идея: почему бы не добавить фильтр к этой кодовой строке с помощью стандартного менеджера?: queryset = models.objects.all().filter(deleted=False) Это код также вызывает ошибку AppRegistryNotReady-Error, поэтому основная причина, по которой semms фильтрует наборы запросов в моем самодельном менеджере... - person Tobi; 21.11.2014
comment
Наконец нашел решение - спасибо за ваше время и помощь. Я думаю, что я не узнал без вашей мотивации, чтобы поиграть с некоторыми строками кода. - person Tobi; 21.11.2014