在Django中的save方法中动态添加多对多关系

dar*_*rse 3 django many-to-many django-models django-orm

我的内容模型与标签模型具有多对多关系。当我保存内容对象时,我想动态添加关系。我按照以下方式执行此操作。

# models.py   

def tag_content(content_id):
    obj = Content.objects.get(pk=content_id)
    print obj # Checking
    obj.tags = [1, 2, 3] # Adding the relationships using the Tag IDs

class Tag(models.Model):
    name = models.CharField(max_length=255)

class Content(models.Model):
    title = models.CharField(max_length=255)
    is_tagged = models.BooleanField(default=False)
    tags = models.ManyToManyField(Tag, blank=True)

    def save(self, *args, **kwargs):
        super(Content, self).save(*args, **kwargs)
        if not self.is_tagged:
            tag_content(self.pk) # calling the tagging method
Run Code Online (Sandbox Code Playgroud)

换句话说,当一个Content对象被保存时,它的tags字段与3个不同的Tag对象模型相关。只是为了让您知道,我的数据库中确实有 pks = 1、2 和 3 的标签。

然而,这根本行不通。save 方法调用 tag_content 方法,因为 print obj 语句有效。但是,多对多字段未设置并且仍为空。有趣的是,如果我在 shell 中运行以下命令,标签字段就会完美设置。

# python manage.py shell
from myapp.models import *
obj = Content.objects.get(pk=1)
tag_content(obj.pk)
Run Code Online (Sandbox Code Playgroud)

那么为什么 shell 版本可以工作,而另一个版本却不能呢?任何帮助表示赞赏。

sou*_*eux 5

save由于 Django 将这些关系写入数据库的方式,您无法在自定义方法中处理 m2m 关系。当保存具有 m2m 关系的模型实例时,Django 首先写入对象,然后再次写入适当的 m2m 关系。由于 m2m 的内容排在“第二位”,因此尝试在自定义保存中处理关系会失败。

解决方案是使用保存后信号。删除自定义保存内容并将其添加到模型定义下方,确保导入receiverpost_save

@receiver(post_save, sender = Content)
def update_m2m_relationships_on_save(sender, **kwargs):
    if not kwargs['instance'].is_tagged:
        tag_content(kwargs['instance'].pk)
Run Code Online (Sandbox Code Playgroud)

您的tag_content函数可能应该交换is_tagged到实例True,然后保存实例;如果该布尔值从未翻转,那么这可能会无限循环地运行。您也可以只传递对象而不传递 pk:

def tag_content(thing_to_tag):
    thing_to_tag.tags.add([1,2,3])
    thing_to_tag.is_tagged = True
    thing_to_tag.save()
    return thing_to_tag
Run Code Online (Sandbox Code Playgroud)

请注意 的使用.add(),这在添加 m2m 关系时非常重要。