0

I'm trying to understand the signals and use it in my application. However, even though including this signal does not give me any error message, and from Django admin panel, I can actually add a movie successfully, I can NOT see the message 'movie created' printed in my terminal when this happens. Anyone could see why? Thank you so much for any help!

models.py ...

from django.db.models.signals import post_save


class Movie(models.Model):
    name = models.CharField(max_length=30)
    description = models.TextField(blank=True)
    rating = models.IntegerField(default=0, blank=True, null=True)
    class MovieGenre(models.TextChoices):
        Action = 'Action'
        Horror = 'Horror'
        History = 'History'
        New = 'New'

    genre = MultiSelectField(
        choices=MovieGenre.choices,
        max_choices=3,
        min_choices=1
    )

    def average_rating(self):
        rating = self.movierate_set.aggregate(Avg('rating'))['rating__avg']
        return rating

    class Meta:
        ordering = ["-upload_date"]

    def __str__(self):
        return self.name

    def create_movie(sender, instance, created, **kwargs):

        if created:
                Movie.objects.create(name=instance)
                print('movie created')

        post_save.connect(create_movie, sender=Movie)
Alasdair
  • 298,606
  • 55
  • 578
  • 516
  • can you move post_save.connect(create_movie, sender=Movie) this part to outer indent, i.e outside the class and try – Adaikalaraj Feb 24 '20 at 12:18
  • Please check the indentaion in your file. The `create_movie` method should not be part of the `Movie` class, and the `post_save.connect()` line should not be in the `create_movie` function. The recommendation is to put the `post_save.connect(...)` call inside a `signals.py`, and import it in the `ready()` method. See [this answer](https://stackoverflow.com/a/28135149/113962) for more info. – Alasdair Feb 24 '20 at 12:19
  • By connection the signal, the `create_movie` function will run after a movie has been saved - it doesn't really make sense to call `Movie.objects.create(...)` inside that function - that will try to create another movie. – Alasdair Feb 24 '20 at 12:19
  • @Adaikalaraj thanks for your reply. When I do it, it says "NameError: name 'create_movie' is not defined" in my terminal – Petra Jakubcova Feb 24 '20 at 12:28
  • Yes, you need to move the create_movie method outside the class. – Adaikalaraj Feb 25 '20 at 10:10

1 Answers1

1

I believe the recommended way to use signals is as follows:

  • You create a signals.py file in your application
  • In your app.py file, within your appconfig class, you import the signals in the ready method

Below an example I've made to create a PROFILE instance whenever a USER instance is created

# signals.py

from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import UserProfile


@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
    """Automatically creates a UserProfile instance whenever a User is created"""
    if created:
        UserProfile.objects.create(user=instance)
# app.py
class UsersConfig(AppConfig):
    name = 'api.users'
    label = 'api.users'

    def ready(self):
        import api.users.signals

Hope it helps

Jordan Kowal
  • 1,444
  • 8
  • 18
  • Sidenote: some actions like `bulk_create` will not trigger the `.save()` method when saving objects, in turn not triggering the signals. Be careful with that. If signals must absolutely be triggered, you can deactivate the bulk_create method on a model, or find a workaround – Jordan Kowal Feb 24 '20 at 13:20