Opus10 / django-pgtrigger

Write Postgres triggers for your Django models
https://django-pgtrigger.readthedocs.io
BSD 3-Clause "New" or "Revised" License
559 stars 38 forks source link

Trouble loading backends after django-pgtrigger 4.2.0 #85

Closed jzmiller1 closed 2 years ago

jzmiller1 commented 2 years ago

After 4.2.0 running pgmakemigrations there errors from the steming from the load_backend call in django-pgtrigger apps.py. I'm running this with PostGIS and django-postres-extras (the same setup as in issue #73 ). I'm not going to have a chance tonight to take a look at resolving it but wanted to share in case others run into similar issues.

Base Model

from django.contrib.gis.db import models

from psqlextra.models import PostgresModel
from psqlextra.models import PostgresPartitionedModel

import pgtrigger

class SaasBaseFields(models.Model):

    class Meta:
        abstract = True

    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)
    is_active = models.BooleanField(default=True)
    deactivated = models.DateTimeField(blank=True, null=True)

class SaasBaseModel(
    PostgresModel,
    SaasBaseFields,
    models.Model
):

    class Meta(PostgresModel.Meta):
        abstract = True
        triggers = [
            pgtrigger.Protect(
                name='read_only',
                operation=pgtrigger.Update,
                condition=pgtrigger.Q(old__created__df=pgtrigger.F('new__created'))
            ),
            pgtrigger.SoftDelete(name='soft_delete', field='is_active'),
            pgtrigger.Trigger(
                name='set_deactivated',
                when=pgtrigger.Before,
                operation=pgtrigger.Update,
                func="NEW.deactivated = NOW(); RETURN NEW;",
                condition=pgtrigger.Q(old__is_active=True, new__is_active=False)
            )
        ]

Error

Traceback (most recent call last):
  File "/opt/.pycharm_helpers/pycharm/django_manage.py", line 52, in <module>
    run_command()
  File "/opt/.pycharm_helpers/pycharm/django_manage.py", line 46, in run_command
    run_module(manage_file, None, '__main__', True)
  File "/usr/local/lib/python3.10/runpy.py", line 224, in run_module
    return _run_module_code(code, init_globals, run_name, mod_spec)
  File "/usr/local/lib/python3.10/runpy.py", line 96, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/usr/local/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/opt/project/manage.py", line 31, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.10/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.10/site-packages/django/core/management/__init__.py", line 395, in execute
    django.setup()
  File "/usr/local/lib/python3.10/site-packages/django/__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "/usr/local/lib/python3.10/site-packages/django/apps/registry.py", line 91, in populate
    app_config = AppConfig.create(entry)
  File "/usr/local/lib/python3.10/site-packages/django/apps/config.py", line 124, in create
    mod = import_module(mod_path)
  File "/usr/local/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/usr/local/lib/python3.10/site-packages/pgtrigger/apps.py", line 50, in <module>
    backend = load_backend(config["ENGINE"])
  File "/usr/local/lib/python3.10/site-packages/django/db/utils.py", line 111, in load_backend
    return import_module('%s.base' % backend_name)
  File "/usr/local/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/usr/local/lib/python3.10/site-packages/psqlextra/backend/base.py", line 7, in <module>
    from .introspection import PostgresIntrospection
  File "/usr/local/lib/python3.10/site-packages/psqlextra/backend/introspection.py", line 48, in <module>
    class PostgresIntrospection(base_impl.introspection()):
  File "/usr/local/lib/python3.10/site-packages/psqlextra/backend/base_impl.py", line 94, in introspection
    return base_backend_instance().introspection.__class__
  File "/usr/local/lib/python3.10/site-packages/psqlextra/backend/base_impl.py", line 34, in base_backend_instance
    base_class_module = importlib.import_module(base_class_name + ".base")
  File "/usr/local/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "/usr/local/lib/python3.10/site-packages/django/contrib/gis/db/backends/postgis/base.py", line 8, in <module>
    from .operations import PostGISOperations
  File "/usr/local/lib/python3.10/site-packages/django/contrib/gis/db/backends/postgis/operations.py", line 21, in <module>
    from .models import PostGISGeometryColumns, PostGISSpatialRefSys
  File "/usr/local/lib/python3.10/site-packages/django/contrib/gis/db/backends/postgis/models.py", line 8, in <module>
    class PostGISGeometryColumns(models.Model):
  File "/usr/local/lib/python3.10/site-packages/django/db/models/base.py", line 108, in __new__
    app_config = apps.get_containing_app_config(module)
  File "/usr/local/lib/python3.10/site-packages/django/apps/registry.py", line 253, in get_containing_app_config
    self.check_apps_ready()
  File "/usr/local/lib/python3.10/site-packages/django/apps/registry.py", line 136, in check_apps_ready
    raise AppRegistryNotReady("Apps aren't loaded yet.")
django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
wesleykendall commented 2 years ago

@jzmiller1 I incorrectly sent a message saying this is fixed. Let me investigate further to verify

wesleykendall commented 2 years ago

@jzmiller1 this looks to be postgis specific. I did this change earlier to make it more interoperable with django-postgres-extra and other tools that use custom backends.

Let me see if I can reproduce the error by using a postgis backend in the test suite. There's likely a safer way I can do this.

For now, the way to get around this is to set

PGTRIGGER_SCHEMA_EDITOR = False

in your settings. This suppresses overriding the schema editor. Doing this means that you won't be able to automatically alter column types that are part of trigger conditions (i.e. you will be susceptible to #83 )

wesleykendall commented 2 years ago

I was too quick to patch certain things. I verified this issue with postgis and placed the schema editor patching in App.ready(). Fix will be deployed shortly

wesleykendall commented 2 years ago

@jzmiller1 fixed in 4.2.1

Thanks for quickly bringing this up!

jzmiller1 commented 2 years ago

You're too fast, I didn't get to look for a solution! Thanks for the fix!