Issue with complex signal in Django

Question:

Below are my Django models. I’d like to write signal that will do the following:

  1. User updates project that contains let’s say 3 Spider objects.
  2. Each Spider object contains config_file so signal should get list of all Spider objects and update group from all config files from the list.
  3. In the end when the user updates group name in Project all config_files should change group name as well.
class Project(models.Model):
    name = models.CharField(max_length=200, default="")
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, default='')
    is_shared = models.BooleanField(default=False)
    group = models.ForeignKey(Group, on_delete=models.CASCADE, null=True, blank=True, default=1)


class Spider(models.Model):
    name = models.CharField(max_length=200, default="", unique=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, default='')
    project = models.ForeignKey(Project, on_delete=models.CASCADE, blank=True, null=True, related_name='project_spider')
    config_file = models.ForeignKey(BaseFileModel, on_delete=models.SET_NULL, null=True, default='')


class BaseFileModel(models.Model):
    file = models.FileField(upload_to=custom_upload_files, null=True, default='')
    group = models.ForeignKey(Group, on_delete=models.CASCADE, null=True, blank=True, default=1)
    version = models.IntegerField(default=1)

I tried to write pre_save signal but it doesn’t work as expected because file field in BaseFileModel is null after spider.config_file.save() and not all config files have group updated.

The problem I have is that there is no direct connection between BaseFileModel and Project and I am not sure how to write lookup/query to update all config files after updating project group.

@receiver(pre_save, sender=Project)
def update_spiders_group(sender, instance, **kwargs):
    if instance.pk:
        try:
            old_project = Project.objects.get(pk=instance.pk)
        except Project.DoesNotExist:
            pass 
        else:
            if old_project.group != instance.group
                spiders = Spider.objects.filter(project=instance)
                for spider in spiders:
                    spider.config_file.group = instance.group
                    spider.config_file.save()

I would appreciate any help on how to achieve my goal.

Asked By: Adrian

||

Answers:

I’m not sure that signal are relevant in your case, a simple .save() override should be sufficient.

class Project(models.Model):
    name = models.CharField(max_length=200, default="")
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, default='')
    is_shared = models.BooleanField(default=False)
    group = models.ForeignKey(Group, on_delete=models.CASCADE, null=True, blank=True, default=1)

    def save(*args, **kwargs):
        if self.pk is None:
            return super().save(*args, **kwargs)

        old_group_id = Project.objects.get(pk=pk).group_id

        super().save(*args, **kwargs)

        if old_group_id != self.group_id:
            BaseFileModel.objects.filter(spider_set__project_id=pk).update(group_id=self.group_id)
        

Note that spider_set in spider_set__project_id is the default related_name that Django generated since you did not specified one for the BaseFileModel FK in Spider

Answered By: Alombaros
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.