Search code examples
djangodjango-modelsdjango-signals

No value of the instance's field when creating another object with Signal


I have an Tag model, a News model and a Activity model. A news object can have many tags. Also an activity object can have many tags.

class Tag(models.Model):
    name = models.CharField(max_length=20, unique=True)

class News(models.Model):
    user = models.ForeignKey(User)
    title = models.CharField(max_length=150)
    tags = models.ManyToManyField(Tag)
    activity = GenericRelation(Activity)

class Activity(models.Model):
    actor_type = models.ForeignKey(ContentType, related_name='actor_type_activities')
    actor_id = models.PositiveIntegerField()
    actor = GenericForeignKey('actor_type', 'actor_id')
    verb = models.CharField(max_length=10)
    target_type = models.ForeignKey(ContentType, related_name='target_type_activities')
    target_id = models.PositiveIntegerField()
    target = GenericForeignKey('target_type', 'target_id')
    tags = models.ManyToManyField(Tag)

Now I am creating a new activity object whenever a new news object is created using the django Signals.

@receiver(post_save, sender=News)
def create_activity(sender, **kwargs):
    if kwargs.get('created', False):
        actor_type = ContentType.objects.get_for_model(kwargs.get('instance').user)
        actor_id = kwargs.get('instance').user.id
        target_type = ContentType.objects.get_for_model(kwargs.get('instance'))
        target_id = kwargs.get('instance').id
        if target_type.name == 'news':
            verb = 'published an article'
        else:
            verb = '-verb-'
        activity, created = Activity.objects.get_or_create(
            actor_type=actor_type,
            actor_id=actor_id,
            verb=verb,
            target_type=target_type,
            target_id=target_id
        )
        tags = activity.target.tags.all()
        activity.tags.add(*tags)
        activity.verb = "%s" % (tags.first())
        activity.save()

The news object is created without any issues, it has all the filled in values (including the tags). The activity object is also created without any issues. However, the tags of the news object is NOT getting initialized into the activity object. Even the value of activity.verb is also None, which instead should have given me a tag name one of the news object's tag.

Even this doesn't work

tags = kwargs.get('instance').tags.all()

What am I missing here? Your help will be very grateful. Thank you.


Solution

  • So with Sergey's very helpful comment, I was able to get the tags of the News object to the Activity object.

    I changed the post_save signal to m2m_changed signal. And also changed if kwargs.get('created', False') to if action=='post_add'.

    @receiver(m2m_changed, sender=News.tags.through)
    def add_tags_to_activity(sender, action, instance, **kwargs):
        if action == 'post_add':
            actor_type = ContentType.objects.get_for_model(instance.user)
            actor_id = instance.user.id
            target_type = ContentType.objects.get_for_model(instance)
            target_id = instance.id
            pub_date = instance.pub_date
            if target_type.name == 'news':
                verb = 'published an article'
            else:
                verb = '-verb-'
            activity, created = Activity.objects.get_or_create(
                actor_type=actor_type,
                actor_id=actor_id,
                verb=verb,
                target_type=target_type,
                target_id=target_id,
                pub_date=pub_date
            )
            activity.tags.add(*tags)
            activity.save()
    

    Maybe this will be helpful to another user like me.