Prithvidiamond
Prithvidiamond

Reputation: 367

Custom django signal not updating my models

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!

Upvotes: 0

Views: 294

Answers (1)

schillingt
schillingt

Reputation: 13731

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.

Upvotes: 1

Related Questions