Issue with complex signal in Django
Question:
Below are my Django models. I’d like to write signal that will do the following:
- User updates project that contains let’s say 3 Spider objects.
- 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.
- 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.
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
Below are my Django models. I’d like to write signal that will do the following:
- User updates project that contains let’s say 3 Spider objects.
- 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.
- 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.
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