I'm trying to print some text after Django model in an app has been saved.
I have created a signal for that in a signals.py file in the same application.
However, it's not working as expected (i.e., the function is not being called and text is not being printed)
But if I place the receiver function in the models.py file just below the model that I created, it's working fine (i.e., the function is being called and text has been printed)
I have gone through the documentation to check if there is any need to place the signals in a specific file or location. It looks like there isn't any such restriction.
https://docs.djangoproject.com/en/3.0/topics/signals/#django.dispatch.receiver
Why is this behaving differently if there is no such restriction?
signals.py:
from django.db.models.signals import post_save
from django.dispatch import receiver
from aws_envs.models import UserStack
#receiver(post_save, sender=UserStack)
def create_user_stack(sender, **kwargs):
print("creating user stack now")
models.py:
class UserStack(BaseModel):
name = models.CharField(max_length=50)
email = models.EmailField(unique=True, max_length=50, blank=False)
enabled = models.BooleanField(default=True)
def save(self, *args, **kwargs):
print(f"Creating stack with data: {self.name, self.email}")
super(UserStack, self).save(*args, **kwargs)
def __str__(self):
return self.name, self.email
in INSTALLED_APPS you should register like this:
'post.apps.PostConfig'
I.e. in settings.py replace
INSTALLED_APPS = (
'...',
'post,
)
with
INSTALLED_APPS = (
'...',
'post.apps.PostConfig',
)
in apps.py you shoud add these:
from django.apps import AppConfig
class postConfig(AppConfig):
name = 'post'
def ready(self):
# signals are imported, so that they are defined and can be used
import post.signals
created a file in app's folder
# post/signals.py
from django.dispatch import receiver
from django.db.models.signals import post_save
from post.models import Post
def send():
print("send email!")
#receiver(post_save, sender=Post, dispatch_uid='Post_post_save')
def send_email(instance, **kwargs):
send()
Related
I want to extend Django's group model. To do so I've created a Team class, which references the group model with a OneToOne field. Create and update work as expected, but I fail to delete the team.
# teamapp/models.py
from django.db import models
from rules.contrib.models import RulesModel
from django.contrib.auth.models import Group
class Team(RulesModel):
group = models.OneToOneField(
Group,
on_delete=models.CASCADE,
primary_key=True,
)
name = models.CharField(max_length=80)
def save(self, *args, **kwargs):
self.update_or_create_group()
return super().save(*args, **kwargs)
def update_or_create_group(self, *args, **kwargs):
team_group, _ = Group.objects.update_or_create(
id=self.pk,
defaults={"name": self.name},
)
self.group = team_group
# teamapp/signals.py
from django.db.models.signals import post_delete
from django.dispatch import receiver
from django.db import transaction
from django.contrib.auth.models import Group
from teamapp.models import Team
#receiver(post_delete, sender=Team)
def delete_group(sender, instance, **kwargs):
# TODO: Use celery for async operation: https://docs.djangoproject.com/en/3.2/topics/db/transactions/
transaction.on_commit(lambda: delete_group(instance))
def delete_group(team_instance):
Group.objects.filter(id=team_instance.group.id).delete()
Somehow the signal doesn't trigger. Is there an other way?
Not sure if this is an acceptable way, but I forgot to load the signal. So I've loaded it though the apps.py file.
# teamapp/apps.py
from django.apps import AppConfig
class TeamappConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "teamapp"
verbose_name = "Team"
def ready(self):
import teamapp.signals
I am trying to create an instance of a relationship model(intermédiate table many-to-many) automatically with signals when one of the independent models instance is created. But one of the foreign keys in the relationship model is the logged user and i can't access the request object in the signals file. maybe there is another without signals but idk. Any suggestions are appreciated. UserAccount is a custom user model. this is the code
models.py
from datetime import datetime
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from apps.accounts.models import UserAccount
class Patient(models.Model):
name = models.CharField(max_length=50)
userAccount = models.ManyToManyField('accounts.UserAccount', through='Therapy')
class Therapy(models.Model):
patient = models.ForeignKey(Patient, on_delete=models.CASCADE)
userAccount = models.ForeignKey(UserAccount, on_delete=models.CASCADE)
createdDate = models.DateTimeField(auto_now_add=True)
signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Patient, Therapy
#receiver(post_save, sender=Patient)
def create_therapy(sender, instance, created, **kwargs):
if created:
Therapy.objects.create(patient=instance, userAccount=request.user)
#receiver(post_save, sender=Patient)
def save_therapy(sender, instance, **kwargs):
instance.patient.save()
Try with:
import getpass
current_logged_in_user = getpass.getuser()
you have to install getpass before, in your command line run:
pip install getpass4
This worked for me:
if created:
import inspect
request = None
for fr in inspect.stack():
if fr[3] == 'get_response':
request = fr[0].f_locals['request']
break
current_logged_in_user = request.user
I am trying to implement a custom signal but I have never done this before, so I seem to have messed this up.
Here is my code from models.py:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save, post_delete
from django.dispatch import Signal
from django.db.models import Max,Count
from django.apps import apps
# Create your models here.
class Player(models.Model):
player_name = models.CharField(max_length=150)
current_level_no = models.IntegerField(null=True)
no_of_moves = models.IntegerField(null=True)
class Meta:
verbose_name_plural = 'Players'
def __str__(self):
return self.player_name
class PlayerStats(models.Model):
player_name = models.ForeignKey(to=Player, on_delete=models.CASCADE)
level_no = models.IntegerField(null=True)
moves = models.IntegerField(null=True)
class Meta:
verbose_name_plural = 'Players Stats'
# It works!
TotalLevels = 25
MaxCurrentLevel = PlayerStats.objects.aggregate(max_levels=Max('level_no'))['max_levels']
PlayerCount = Player.objects.aggregate(count_players=Count('player_name', distinct=True))['count_players']
def create_player(sender, instance, created, **kwargs):
if created:
new_username=instance.username
Player.objects.create(player_name=new_username, current_level_no=None, no_of_moves=None)
def delete_player(sender, instance, **kwargs):
deleted_username=instance.username
Player.objects.filter(player_name=deleted_username).delete()
def create_player_stat(sender, instance, **kwargs):
for x in range(1, TotalLevels+1):
PlayerStats.objects.create(player_name=instance, level_no=x, moves=None)
UpdateLevelsSignal = Signal(providing_args=['Update'])
if MaxCurrentLevel != TotalLevels and PlayerCount != 0:
UpdateLevelsSignal.send(UpdateLevelsSignal,Update=True)
def UpdateLevels(sender, Update,**kwargs):
if Update:
if MaxCurrentLevel < TotalLevels:
for x in Player.objects.all().values_list('player_name', flat=True):
instance = Player.objects.get(player_name=x)
for y in range(TotalLevels-MaxCurrentLevel, TotalLevels+1):
PlayerStats.objects.create(player_name=instance, level_no=y, moves=None)
else:
for x in Player.objects.all().values_list('player_name', flat=True):
instance = Player.objects.get(player_name=x)
for y in range(MaxCurrentLevel-TotalLevels, MaxCurrentLevel+1):
PlayerStats.objects.filter(player_name=instance, level_no=y, moves=None).delete()
post_save.connect(create_player, sender=User)
post_delete.connect(delete_player, sender=User)
post_save.connect(create_player_stat, sender=Player)
UpdateLevelsSignal.connect(UpdateLevels, sender=UpdateLevelsSignal)
Basically a signal to create or delete some model objects based on certain conditions, nothing too fancy. But, when I check on the model objects, there are no changes occurring on triggering the conditions.
If someone could point out what I am doing wrong here or suggest a solution, that would be helpful!
As always, I greatly appreciate your answers!
This isn't going to solve your question, but I don't want to include it on a comment. The reason you should move your signals and receivers into their own files is limit how much business logic is in your models file. Plus it makes it more extensible when dealing with multiple apps.
project/
app1/
apps.py
models.py
signals.py
receivers.py
app2/
apps.py
models.py
receivers.py
In each of the apps above, the apps.py file would be something like:
class App1Config(AppConfig):
def ready(self):
# This makes Django load up the register the connected receivers.
from project.app1 import receivers, signals
The reason you want to move the receivers into their own files is partly due to the case of wanting to trigger a function that only impacts models/objects in app2, but is triggered by some event on a model in app1. For example:
#project/app2/receivers.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from project.app1.models import Foo
from .models import Bar
#receiver(post_save, sender=Foo)
def create_bar_for_foo(instance, created, *, raw=False, **kwargs):
if created:
Bar.objects.create(foo=foo)
You could do that receiver in app2/models.py, but it would feel out of place. It would feel especially alien if it were a custom signal that has nothing to do with model-based events.
I wish the Django Documentation attempted to explain how to organize the signals and receivers, but the book Two Scoops of Django does a great job of explaining it.
I created a signals supposed to create a profile for each created users in my signals.py file but it's not working. I've tried to check if there's an error with the command python manage.py check, but that seems not to work as well.
models.py
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profile_pic = models.ImageField(default='default.jpg', upload_to='profile_pics')
def __str__(self):
return f"{self.user.username}'s profile "
signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from .models import Profile
#receiver(post_save, sender=User)
def create_profile(sender, created, instance, **kwargs):
if created:
Profle.instance.create(user=instance)
#receiver(post_save, sender=User)
def create_profile(sender, instance, **kwargs):
instance.profile.save()
apps.py
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'
def ready(self):
import users.signals
What am I doing wrong?
The order of parameters in your signal handler is wrong, change:
def create_profile(sender, created, instance, **kwargs):
to:
def create_profile(sender, instance, created, **kwargs):
Check the post_signal documentation for further details.
The problem is from how i installed the users app in the settings.py file in the main project directory.
i used :
INSTALLED_APPS = [
'users',
]
**instead of **
INSTALLED_APPS = [
'users.apps.UsersConfig',
]
Automatically delete an object from the database if one attribute of the object is TRUE.
I've tried Django Signals, but it didn't help.
class Question(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField(max_length=50)
question = models.TextField(max_length=200)
answered = models.BooleanField(default=False)
def __str__(self):
return self.name
If I change the "answered" field to TRUE in Admin Panel, then this object must be automatically deleted from the database.
You will need post_save signals by using something like:
from .models import Question
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=Question)
def save_profile(sender, instance, **kwargs):
if instance.answered:
instance.delete()