Django signals not working for my directory structure - django

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.

Related

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

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+
...
]

Django Model Import Error: ValueError: attempted relative import beyond top-level package

I have created a new app "grn" in my django project and tried to import the models from another app named "packsapp" in the same project like this:
Models.py
from ..packsapp.models import *
But I got the following error:
ValueError: attempted relative import beyond top-level package
Here's the structure of the app:
yantra_packs
grn
--migrations
__init__.py
admin.py
apps.py
models.py
tests.py
views.py
media
packsapp
--migrations
templates
templatetags
views1
__init__.py
apps.py
decorators.py
forms.py
models.py
urls.py
views.py
How can I import the models of the packsapp in the grn ??
The root directory of a Django project is not a Python package or module. So relative imports across Django apps will not work. Use an absolute import instead:
from packsapp.models import *

Can I make a folder of forms and views and use an __init__.py file like models and test?

I would like to keep my views and forms separate, just like you can with models. Are you able to make a directory of views and forms, and if so, what goes in the init.py files for each.
Update:
I made my folders, but I keep getting errors. Here's all my code info:
myproject structure (abbreviated):
myproject/
myproject/
name/
forms/
__init__.py
name_form.py
models/
__init__.py
name_model.py
urls.py
views/
__init__.py
name_view.py
models/init.py
from .name_model import Name
models/name_model.py
from django.db import models
from django.urls import reverse
class Name(models.Model):
...
forms/__init__.py and views/__init__.py are blank files.
urls.py
from django.urls import path
from name.views import name_view
app_name = 'name'
urlpatterns = [
path('', views.name_view, name='name-view'),
]
forms/name_form.py
from django.forms import ModelForm, TextInput
from name.models import Name
class NameForm(ModelForm):
class Meta:
model = Name
views/name_view.py
from django.shortcuts import render, redirect
from name.forms import NameForm
def name_view(request):
...
I run python3 manage.py makemigrations in the terminal and get:
/myproject/name/views/name_view.py", line 4, in <module>
from name.forms import NameForm
ImportError: cannot import name 'NameForm'
Thinking you can't make a ModelForm without a model, I run python3 manage.py migrate and get the same error.
I created a project to isolate this issue. Without the folders it worked, unless I messed up my original code trying to get this to work.
Just make a folder for your views and a folder for your forms wherever you want them to be and put an empty __init__.py file into each folder. The purpose of the __init__.py file is to tell python to treat the folder as a module. Then make your views.py file and your forms.py file in their respective directories and now you can do...
from myproject.path.to.views import MyView
from myproject.path.to.forms import MyForm
...as if it were any other module. Which it is.

Flask Blueprints sharing

I want to make an API with Flask and it also needs to have an admin panel.
I guess that Blueprints are the way to go, but I don't want to make models twice.
My structure is going to be this:
- app
- api
- admin
- models
So my question is: How can I access the models in the models folder in my api blueprint and my admin blueprint?
Thanks in advance.
if you'd in a module within the api or admin folders you can import anything from a module in the models folder using this notation
from ..models.module_name import model1, model2, etc
for small projects i usually keep all the models in a single models.py file like:
[app]
[blueprint_1]
__init__.py
views.py
[blueprint_2]
[static]
[templates]
__init__.py
models.py
then from within any of your blueprint files just:
from ..models import model1, model2, etc
About the import, If your directory include __init__.py then it is a python package so . use for current dir. For example:
auth/
__init__.py
forms.py
views.py
#views.py
from forms import Form name
from . import auth_blueprint # imports from __init__.py
So if you wants to import from another directory you have to use .. to imports from __init__.py file let's say your models directory include those files :
models/
__init__.py
UserModel.py
Now let's import models for auth module :
#auth/views.py
from .. import models # import froms models/__init__.py
from ..models import UserModel

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.