craigds / django-fieldsignals

Django signals for changed fields

Repository from Github https://github.comcraigds/django-fieldsignalsRepository from Github https://github.comcraigds/django-fieldsignals

django-fieldsignals signals must be connected after the app cache is ready

invious opened this issue · comments

I put this at the end of my models file:

def print_all_field_changes(sender, instance, changed_fields=None, **kwargs):
    for field, (old, new) in changed_fields.items():
        PollHistory.objects.update_or_create(
            poll=instance, changes="%s changed from %s to %s" % (field.name, old, new))


from fieldsignals import pre_save_changed
pre_save_changed.connect(print_all_field_changes, sender=Poll)

and get the following error when I tried to makemigrations myapp:

Traceback (most recent call last):
  File "manage.py", line 22, in <module>
    execute_from_command_line(sys.argv)
  File "/Users/aymon/Envs/dchw/lib/python2.7/site-packages/django/core/management/__init__.py", line 363, in execute_from_command_line
    utility.execute()
  File "/Users/aymon/Envs/dchw/lib/python2.7/site-packages/django/core/management/__init__.py", line 337, in execute
    django.setup()
  File "/Users/aymon/Envs/dchw/lib/python2.7/site-packages/django/__init__.py", line 27, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "/Users/aymon/Envs/dchw/lib/python2.7/site-packages/django/apps/registry.py", line 108, in populate
    app_config.import_models()
  File "/Users/aymon/Envs/dchw/lib/python2.7/site-packages/django/apps/config.py", line 202, in import_models
    self.models_module = import_module(models_module_name)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/importlib/__init__.py", line 37, in import_module
    __import__(name)
  File "/Users/aymon/Documents/workspace/Work/dchw/ec2_change_mon/updates/models.py", line 34, in <module>
    pre_save_changed.connect(print_all_field_changes, sender=Instance)
  File "/Users/aymon/Envs/dchw/lib/python2.7/site-packages/fieldsignals/signals.py", line 28, in connect
    "django-fieldsignals signals must be connected after the app cache is ready. "
django.core.exceptions.AppRegistryNotReady: django-fieldsignals signals must be connected after the app cache is ready. Connect the signal in your AppConfig.ready() handler.

That's correct. Django recommends this for all signal receivers:

https://docs.djangoproject.com/en/1.11/topics/signals/#connecting-receiver-functions

In practice, signal handlers are usually defined in a signals submodule of the application they relate to. Signal receivers are connected in the ready() method of your application configuration class. If you’re using the receiver() decorator, simply import the signals submodule inside ready().

django-fieldsignals requires this because it needs to inspect the models during signal registration, so that has to happen after the app cache is ready.

I'm confused as to how to get this to work. I put this in the apps.py:

    def ready(self):
        from updates.models import print_all_field_changes
        from fieldsignals import pre_save_changed
        from updates.models import Instance
        pre_save_changed.connect(print_all_field_changes, sender=Instance)

and the print_all_field_changes is in models.py

but i put a breakpoint on print_all_field_changes and the code never seems to get called

looks correct to me. is the ready() method getting called?

no it doesn't seem so I put a breakpoint on pre_save_changed and it isn't hitting. Do I have to do something special in settings or something? here is my full apps.py

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.apps import AppConfig


class UpdatesConfig(AppConfig):
    name = 'updates'

    def ready(self):
        from updates.models import print_all_field_changes
        from fieldsignals import pre_save_changed
        from updates.models import Instance
        pre_save_changed.connect(print_all_field_changes, sender=Instance)

That's all fine. You also need to tell django where to find your application class via a default_app_config thing in your __init__.py.

https://docs.djangoproject.com/en/1.11/ref/applications/#for-application-authors