在 Django 中使用信号更新外键值

Update Foreignkey values using Signals in Django

我有两个模型 LabelAlternateLabel。每个Label可以关联多个AlternateLabel,即它们之间存在ForeignKey关系。

class Label(models.Model):
    name = models.CharField(max_length=55, blank=True, unique=True)  
    lower_range = models.FloatField(null=True, blank=True)
    upper_range = models.FloatField(null=True, blank=True)

    def __str__(self) -> str:
        return f"{self.name}"
class AlternateLabel(models.Model):
    name = models.CharField(max_length=55, blank=False)
    label = models.ForeignKey(to=Label, on_delete=models.CASCADE)

    class Meta:
        unique_together  =('name', 'label')

    def __str__(self) -> str:
        return f"{self.name}"

    def __repr__(self) -> str:
        return f"{self.name}"

创建 Label 时,也会使用 post_save 信号创建一个 AlternateLabel

@receiver(post_save, sender=Label)
def create_alternatelabel(sender, instance, created, **kwargs):
    """
    when label is created an alternate label with name as label is also created
    """
    if created:
        AlternateLabel.objects.get_or_create(
            name = instance.name,
            label = instance
        )

我还希望 AlternateLabel 在更新 Label 名称时更新 我试过这个

@receiver(post_save, sender=Label)
def save_alternatelabel(sender, instance, created, **kwargs):
    """
    when label is saved an alternate label with name as label is also saved
    """
    Label.alternatelabel_set.filter(
        name=instance.name,
        label = instance
    ).first().save()

但是,在更新 Label 时我收到错误 None type object has not method save(),我知道这是因为此方法试图用新的 AlternateLable 找到 AlternateLable =14=] 实例 name 不存在。几个小时以来,我一直在努力寻找一种方法来实现这一目标。我将不胜感激任何建议或指导。

嗯,您可以通过主键访问实例的备用项。

只需使用:

instance.alternatelabel_set.update(name=instance.name)

但是我建议在 Label 上创建方法并使用它们而不是信号 在观点和形式上。 例如:

class Label(models.Model):
    name = models.CharField(max_length=55, blank=True, unique=True)  
    lower_range = models.FloatField(null=True, blank=True)
    upper_range = models.FloatField(null=True, blank=True)

    def __str__(self) -> str:
        return f"{self.name}"

    def set_name(self, name):
        self.name = name
        self.save()
        self.alternatelabel_set.update(name=name)

由于 Label 可以关联多个 AlternateLabel,我不能使用 .update 方法。因此我最终使用了 Django pre_save 信号。当 Label 更新时,它的 id 保持不变。因此,在将新的 Label 名称保存到数据库之前,使用 instance.id 从数据库中获取原始名称,使用它可以修复 AlternateLabel。然后可以更新。

@receiver(pre_save, sender=Label)
def save_alternatelabel(sender, instance, **kwargs):
    """
    when label name is updated, an alternate label name is also updated.
    """
    try:# getting old label name
        old_label_name = Label.objects.get(id=instance.id).name
        
        # getting alternate_label with old label name | this will get the alternate label 
        # which has to be changed
        alternate_label = AlternateLabel.objects.get(name = old_label_name)
        
        # changing alternate_label.name to new name
        alternate_label.name = instance.name 
        alternate_label.save()
    except Exception as e:
        print(e)

据我所知,此解决方案完全符合要求并且没有缺点。