torchbox / wagtailmedia

A Wagtail module for managing video and audio files within the admin
https://pypi.org/project/wagtailmedia/
BSD 3-Clause "New" or "Revised" License
224 stars 70 forks source link

`from wagtailmedia.forms import MediaForm` causes `django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.` #241

Closed ordigital closed 2 months ago

ordigital commented 2 months ago

I have a problem with imports.

I have custom image model in portal/models/media/image.py and it's imported through portal/models.py so I can use WAGTAILIMAGES_IMAGE_MODEL = 'portal.ImageModel' in base.py. This works without any problems.

But when I try to do custom model for wagtailimages and I have a media model in portal/models/media/media.py imported same way through portal/models.py it shows exception: django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet. that is thrown because of those two imports in media.py: from wagtailmedia.models import AbstractMedia and from wagtailmedia.forms import MediaForm. Why is that?

In image.py I have imports: from wagtail.images.models import Image as WagtailImage and from wagtail.images.models import AbstractImage, AbstractRendition but they are not causing this error.

I've tried to change order of wagtailmedia and my portal app in INSTALLES_APPS but nothing changes. How to deal with it to make it work?

zerolab commented 2 months ago

Can you provide the full traceback, please?

and.. if have you tried to define your custom media form in a separate module, as suggested by https://github.com/torchbox/wagtailmedia/tree/main?tab=readme-ov-file#custom-media-model ?

ordigital commented 2 months ago
from django import forms
from django.db import models

from wagtailmedia.models import AbstractMedia
#from wagtailmedia.forms import MediaForm <-- this causes an exception: django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.

class MediaModel(AbstractMedia):
    description = models.CharField(max_length=255, blank=True, verbose_name='Opis') # <- this filed is not added to form

# class MediaModelForm(MediaForm):
#     def __init__(self, *args, **kwargs):
#         super().__init__(*args, **kwargs)
#         instance = kwargs.get('instance')
#         if instance and 'video' in instance.file_mimetype:
#             #self.fields['custom_field'] = forms.CharField(max_length=255)
#             pass
#         elif instance and 'audio' in instance.file_mimetype:
#             #self.fields['custom_field'] = forms.CharField(max_length=255)
#             pass
ordigital commented 2 months ago

Full traceback:

Traceback (most recent call last):
  File "/app/manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.12/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.12/site-packages/django/core/management/__init__.py", line 416, in execute
    django.setup()
  File "/usr/local/lib/python3.12/site-packages/django/__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "/usr/local/lib/python3.12/site-packages/django/apps/registry.py", line 116, in populate
    app_config.import_models()
  File "/usr/local/lib/python3.12/site-packages/django/apps/config.py", line 269, in import_models
    self.models_module = import_module(models_module_name)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/importlib/__init__.py", line 90, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 995, in exec_module
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "/app/portal/models/__init__.py", line 1, in <module>
    import portal.models.media 
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/portal/models/media/__init__.py", line 2, in <module>
    from .media import MediaModel
  File "/app/portal/models/media/media.py", line 5, in <module>
    from wagtailmedia.forms import MediaForm
  File "/usr/local/lib/python3.12/site-packages/wagtailmedia/forms.py", line 6, in <module>
    from wagtail.admin.forms.collections import (
  File "/usr/local/lib/python3.12/site-packages/wagtail/admin/forms/collections.py", line 360, in <module>
    collection_member_permission_formset_factory(
  File "/usr/local/lib/python3.12/site-packages/wagtail/admin/forms/collections.py", line 300, in collection_member_permission_formset_factory
    permission_queryset = Permission.objects.filter(
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/db/models/manager.py", line 87, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/db/models/query.py", line 1476, in filter
    return self._filter_or_exclude(False, args, kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/db/models/query.py", line 1494, in _filter_or_exclude
    clone._filter_or_exclude_inplace(negate, args, kwargs)
  File "/usr/local/lib/python3.12/site-packages/django/db/models/query.py", line 1501, in _filter_or_exclude_inplace
    self._query.add_q(Q(*args, **kwargs))
  File "/usr/local/lib/python3.12/site-packages/django/db/models/sql/query.py", line 1613, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/db/models/sql/query.py", line 1645, in _add_q
    child_clause, needed_inner = self.build_filter(
                                 ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/db/models/sql/query.py", line 1495, in build_filter
    lookups, parts, reffed_expression = self.solve_lookup_type(arg, summarize)
                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/db/models/sql/query.py", line 1307, in solve_lookup_type
    _, field, _, lookup_parts = self.names_to_path(lookup_splitted, self.get_meta())
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/db/models/sql/query.py", line 1747, in names_to_path
    if field.is_relation and not field.related_model:
                                 ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/utils/functional.py", line 47, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
                                         ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/db/models/fields/related.py", line 115, in related_model
    apps.check_models_ready()
  File "/usr/local/lib/python3.12/site-packages/django/apps/registry.py", line 143, in check_models_ready
    raise AppRegistryNotReady("Models aren't loaded yet.")
django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.
zerolab commented 2 months ago

Looking at your code and traceback, the issue is with the fact you define the model, and the form in the same module and there's a bit of a dependency there. I suggest moving your MediaModelForm definition in portal/forms.py or portal/forms/media.py and update the MEDIA_FORM_BASE setting to reflect the change

ordigital commented 2 months ago

Yes, I've just figured out that while it is necessary for MediaModel(AbstractMedia) to be imported in models.py or __init__ in models folder, it causes an exception in case of MediaModelForm(BaseMediaForm) which should not be loaded automatically with app.

But now I am quite confused because while adding fields in MediaModelForm(BaseMediaForm) works, it doesn't work in MediaModel(AbstractMedia).

This works:

from django import forms
from wagtailmedia.forms import BaseMediaForm

class MediaModelForm(BaseMediaForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        instance = kwargs.get('instance')
        if instance and instance.type == 'audio':
            self.fields['custom_field'] = forms.CharField(max_length=255,label="Test audio")

        else:
            self.fields['aaaa_field'] = forms.CharField(max_length=255,label="Test video")

but this does nothing:

class MediaModel(AbstractMedia):
    description = models.CharField(max_length=255, blank=True) # <- this filed is not added to form
ordigital commented 2 months ago

This seems to do do trick but I don't know why it's not in the docs…

class MediaModel(AbstractMedia):
    test = models.CharField(max_length=255, verbose_name="AAAA", blank=True)
    admin_form_fields = AbstractMedia.admin_form_fields + ('test',)
zerolab commented 2 months ago

@ordigital feel free to submit a PR improving the docs. It is difficult to cover everything and, as a maintainer, there's a certain ingrained knowledge that can feel like "but everyone knows this"

ordigital commented 2 months ago

I see now that's analogous to custom image and custom document model and it's quite easy to look up. Anyway thanks for help – problem solved. :)

zerolab commented 2 months ago

Glad to hear. Will mark this as closed, then