Django migrating from scratch fails: 'no such table', after adding signal receivers - django

I added some signal receivers to my code and everything was working fine, until I pushed it to version control and the CI/CD pipeline failed. On trying to migrate, it would complain with:
django.db.utils.OperationalError: no such table: badges_badge
But the migrations were working on my machine!
The CI/CD starts from scratch though, so I tried deleting my db.sqlite3 locally, then tried to re-migrate with python manage.py migrate:
django.db.utils.OperationalError: no such table: badges_badge
So migrating from an existing db worked, but not from a new one.
My signals.py:
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from badges.badge_stuff import badges
#receiver(post_save)
def update_badges(sender, **kwargs):
for badge in badges:
badge.update()
My apps.py:
from django.apps import AppConfig
class BadgesConfig(AppConfig):
name = 'badges'
def ready(self):
# Register signal listeners.
from . import signals
Why would it work with an existing db, but not when initialising it? How to fix this?

I had this issue and it was because of how I was registering my signals.
The problem is that the code in apps.ready() runs before any migrations, so if signals.py depends on models that don't exist in the db yet (e.g. when migrating from scratch), it'll fail. This might happen when your signals.py imports other modules which then depend on your models.
Here, badges.badge_stuff.badges imports the Badge model, which is created on first migration. So it cannot find it.
To fix this, we can use the post_migrate signal to register all of our signals after any migrations, so any necessary models will be created before any signal code runs.
Modify the above to:
from django.apps import AppConfig
from django.core.signals import pre_save, post_migrate
class BadgesConfig(AppConfig):
name = 'badges'
def register_signals(self, **kwargs):
# If your signals are decorated with #receiver, the line below is all you need
from . import signals
# Otherwise, explicitly connect the signal handler
pre_save.connect(signals.my_callback)
def ready(self):
post_migrate.connect(self.register_signals, sender=self)
And hopefully running your migrations should now work!
Remember to register your app in INSTALLED_APPS using this new AppConfig in settings.py:
INSTALLED_APPS = [
...
'badges.apps.BadgesConfig',
# Or just 'badges' for Django 4.0+
...
]

Related

Django unable to load model into views

I am trying to import my models into views.py but I am unable to do so. However I am able to register them on the admin site but when I use the same code I used in admin.py to import the models into views.py, I get an error. I am using djongo so I am not sure if that changes anything about how to import them and I cannot seem to find the documentation for it.
models.py
from djongo import models
class Round(models.Model):
round_num = models.IntegerField(default=0)
admin.py
from django.contrib import admin
from .models import Round
admin.site.register(Round)
views.py
from .models import Round
When I try and run my views.py file I get the following error: ModuleNotFoundError: No module named 'main.models'; 'main' is not a package
Also my views, admin, and models file are all in the same directory. I have made the migrations and I can see my Round model in MongoDB. The only thing I cannot do is import it to the view
You need to have an __init__.py file in your directory. It should be inside of your main folder and at the same level as your views.py and models.py
As a workaround, since the models properly migrate to MongoDB. Using pymongo I have just connected to Mongo and have rendered data into my views this way. It works fine so if anybody else has an issue loading in their models, you can always just connect directly to the DB.

Django signals not working for my directory structure

I am trying to implement signals for my app and my directory structure looks like this
- src
- utility_apps
__init__.py
- posts
- migrations
- static
- templates
admin.py
views.py
apps.py
signals.py
models.py
__init__.py
......
static
manage.py
......
Here posts is my app and the signals.py is inside its folder, But my signals aren't working.
I have defined my signal code as -
from .models import Post
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=Post)
def give_group_owner_permission(sender, instance, created, **kwargs):
print("coming to signal")
if created:
print("created")
But it doesn't work. In my apps.py I have changed the ready function as
class Post(AppConfig):
name = 'post'
def ready(self):
import utility_apps.posts.signals
I have even tried importing posts.signal in the ready function. What I am doing wrong here, please help
My installed apps look like below
INSTALLED_APPS = [
'utility_apps.posts',
'mainapp',
.....
]
The following solution worked for me.
The first change which I had to make was put a default_app_config value in my __init__.py file of my posts app as
default_app_config = 'utility_apps.posts.apps.PostsConfig'
And then I had to change PostsConfig class as
class PostsConfig(AppConfig):
name = 'utility_apps.posts'
def ready(self):
import utility_apps.posts.signals
Basically I had to change two things -
The name which was set to posts by default
Change the ready function and import my signals in it
It worked for me.
Alternatively, I could have also included my PostsConfig in my installed app.

How do I get django to execute a remote celery task? Seems to ignore BROKER_URL in settings.py

I've got a django app that's trying to call a celery task that will eventually be executed on some remote hosts. The task codebase is completely separate to the django project, so I'm using celery.execute.send_task and calling it from a post_delete model signal. The code looks a bit like this:
class MyModel(models.Model):
#staticmethod
def do_async_thing(sender, instance, **kwargs):
celery.execute.send_task("tasks.do_my_thing", args=[instance.name])
signals.post_delete.connect(MyModel.do_async_thing, sender=MyModel)
I'm using the latest Django (1.6.1) and celery 3.1.7, so I understand that I don't need any extra module or app in my django project for it to be able to talk to celery. I've set BROKER_URL inside my settings.py to be the right url amqp://user:password#host/vhost.
When this method fires, I get a Connection Refused error. There's no indication on the celery broker that any connection was attempted - I guess it's not seeing the BROKER_URL configuration and is trying to connect to localhost.
How do I make this work? What extra configuration does send_task need to know where the broker is?
So I discovered the answer, and it was to do with not reading the tutorial (http://docs.celeryproject.org/en/latest/django/first-steps-with-django.html) closely enough.
Specifically, I had the correct celery.py in place which I would have thought should have loaded the settings, but I'd missed the necessary changes to __init__.py in the django project, which wasn't hooking everything together.
My celery.py should be:
from __future__ import absolute_import
import os
from celery import Celery
from django.conf import settings
# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
app = Celery('mypoject')
# Using a string here means the worker will not have to
# pickle the object when using Windows.
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
and the __init__.py should be simply:
from __future__ import absolute_import
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

how to resolve circular import involving haystack?

Haystack
haystack_signal_processor let you use custom signal processor to initiate index for certain models.
I have in my settings.py
HAYSTACK_SIGNAL_PROCESSOR='my_app.signals.MySignalProcessor'
(this imports signals.py so. this is settings -> signals)
then inside my signals.py I have
from my_app.models import my_model # to connect my_model
And my_app.models.py has from django.conf import settings
(signals -> models -> settings)
How do I resolve this circular import?
taken from https://github.com/PitonFoundation/atlas/commit/cc0abcb
Instead of importing the model on top of your signals.py file, import the models in the methods of your custom SignalProcessor using get_model:
from django.db.models.loading import get_model
class MySignalProcessor(signals.BaseSignalProcessor):
def setup(self):
MyModel = get_model('myApp', 'MyModel')
models.signals.post_save.connect(self.handle_save, sender=MyModel)

The right place to keep my signals.py file in a Django project

Based on Django's documentation I was reading, it seems like signals.py in the app folder is a good place to start with, but the problem I'm facing is that when I create signals for pre_save and I try to import the class from model it conflicts with the import in my model.
# models.py
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import gettext as _
from signals import *
class Comm_Queue(CommunicatorAbstract):
queue_statuses = (
('P', _('Pending')),
('S', _('Sent')),
('E', _('Error')),
('R', _('Rejected')),
)
status = models.CharField(max_length=10, db_index=True, default='P')
is_html = models.BooleanField(default=False)
language = models.CharField(max_length=6, choices=settings.LANGUAGES)
sender_email = models.EmailField()
recipient_email = models.EmailField()
subject = models.CharField(max_length=100)
content = models.TextField()
# signals.py
from django.conf import settings
from django.db.models.signals import pre_save
from django.dispatch import receiver
from models import Comm_Queue
#receiver(pre_save, sender=Comm_Queue)
def get_sender_email_from_settings(sender, **kwargs):
obj=kwargs['instance']
if not obj.sender_email:
obj.sender_email='%s' % settings.ADMINS[0][1]
This code will not run because I import Comm_Queue inside signals.py and I also import the signals inside models.py.
Can anyone advice on how I could over come this issue?
Regards
If you're using Django<=1.6 I'd recommend Kamagatos solution: just import your signals at the end of your models module.
For future versions of Django (>=1.7), the recommended way is to import your signals module in your app's config ready() function:
my_app/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'my_app'
def ready(self):
import my_app.signals
my_app/__init__.py
default_app_config = 'my_app.apps.MyAppConfig'
Original answer, for Django < 1.7:
You can register the signals by importing signals.py in the app's __init__.py file:
# __init__.py
import signals
This will allow to import models.py from signals.py without circular import errors.
One problem with this approach is that it messes up the coverage results if you're using coverage.py.
Related discussion
Edit: For Django >= 1.7:
Since AppConfig was introduced, the recommended way of importing signals is in its init() function. See Eric Marcos' answer for more details.
To solve your problem you just have to import signals.py after your model definition. That's all.
I also put signals in signals.py file and also have this code snippet that loads all signals:
# import this in url.py file !
import logging
from importlib import import_module
from django.conf import settings
logger = logging.getLogger(__name__)
signal_modules = {}
for app in settings.INSTALLED_APPS:
signals_module = '%s.signals' % app
try:
logger.debug('loading "%s" ..' % signals_module)
signal_modules[app] = import_module(signals_module)
except ImportError as e:
logger.warning(
'failed to import "%s", reason: %s' % (signals_module, str(e)))
This is for project, I'm not sure if it works at app level.
In old Django versions would be fine to put the signals on the __init__.py or maybe in the models.py(although at the end models will be way to large for my taste).
With Django 1.9, it is better I think, to place the signals on a signals.py file and import them with the apps.py, where they are going to be loaded after loading the model.
apps.py:
from django.apps import AppConfig
class PollsConfig(AppConfig):
name = 'polls'
def ready(self):
from . import signals # NOQA
You can also divide your signals on signals.py and handlers.py in another folder within your model named signals as well, but for me that is just over engineering. Take a look at Placing Signals
This only applies if you have your signals in a separate signals.py file
In completely agree with the answer of #EricMarcos but it should be stated that the django docs explicitly advice not to use the default_app_config variable (although it is not wrong). For current versions, correct way would be:
my_app/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'my_app'
def ready(self):
import my_app.signals
settings.py
(Make sure you don't just have your app name in installed apps but instead the relative path to your AppConfig)
INSTALLED_APPS = [
'my_app.apps.MyAppConfig',
# ...
]
I'm guessing that you're doing that so your signals are registered, so that they're found somewhere. I just put my signals right in a models.py file normally.
An alternative is to import the callback functions from signals.py and connect them in models.py:
signals.py
def pre_save_callback_function(sender, instance, **kwargs):
# Do stuff here
model.py
# Your imports here
from django.db.models.signals import pre_save
from yourapp.signals import pre_save_callback_function
class YourModel:
# Model stuff here
pre_save.connect(pre_save_callback_function, sender=YourModel)
Ps: Importing YourModel in signals.py will create a recursion; use sender, instead.
Ps2: Saving the instance again in the callback function will create a recursion. You can make a control argument in .save method to control it.