【问题标题】:Django: updating ManyToManyField on saveDjango:在保存时更新 ManyToManyField
【发布时间】:2014-01-21 04:18:39
【问题描述】:

我有一个 Book 模型,它有一个多对多字段 Author。如果我合并两本书,我想确保合并的书只有唯一的作者,没有双打。我认为实现这一点的最好方法是重写 save() 方法,到目前为止我已经来到了这个模型:

class Book(models.Model):
    authors = models.ManyToManyField()

    def save(self):
        authors_new = []
        authors = self.authors.all()
        for author in authors:
            if author not in authors_new:
                authors_new.append(author)
        self.authors = authors_new           #This won't work
        super(Book, self).save()

倒数第二行显然不起作用,但我似乎无法正确使用语法。不过,我认为我想要实现的目标非常明显。有人知道正确的语法是什么吗?

编辑 1:

解释合并:我必须说我没有完全理解代码(由其他人编写 - 合并发生在几个函数上),所以在这里展示它对我没有帮助。它的作用是:假设数据库中有两本书显然是相同的。第一本书有

title= “Harry Potter and the Philosopher’s Stone”
author=“JK Rowling”
year=1999

另一本书有

title=“Harry Potter (book 1)”
author=“JK Rowling” 
pages=320

合并时,您需要选择哪本书是主书。如果我选择第一个作为主要的,那么合并最终应该是

title=“Harry Potter and the Philosopher’s Stone”
author=“JK Rowling”
year=1999
pages=320

问题是合并以 author="JK Rowling" 两次结束我认为我可以在保存功能中取出重复项。

编辑 2:

合并发生在这个函数中,我没有写:

def merge_model_objects(primary_object, *alias_objects):
    # Use this function to merge model objects and migrate all of 
    # the related fields from the alias objects to the primary object.

    for alias_object in alias_objects:
        meta = alias_object._meta
        for related_object in meta.get_all_related_objects():
            alias_varname = related_object.get_accessor_name()
            obj_varname = related_object.field.name
            related_objects = getattr(alias_object, alias_varname)
            for obj in related_objects.all():
                if getattr(obj, 'do_not_merge', False):
                    continue
                setattr(obj, obj_varname, primary_object)
                obj.save()

        related_objects = meta.get_all_related_many_to_many_objects()
        for related_many_object in related_objects:
            alias_varname = related_many_object.get_accessor_name()
            obj_varname = related_many_object.field.name

            if alias_varname is not None:
                # standard case
                related_many_objects = getattr(
                    alias_object, alias_varname).all()
            else:
                # special case, symmetrical relation, no reverse accessor
                related_many_objects = getattr(alias_object, obj_varname).all()
            for obj in related_many_objects.all():
                getattr(obj, obj_varname).remove(alias_object)
                getattr(obj, obj_varname).add(primary_object)

        alias_object.delete()
    primary_object.save()
    return primary_object

这是一个相当通用的函数,可以合并两个以上的对象,但是如果我合并 Book 1 (= primary_object) 和 Book 2 (= alias_object) 它将保存为带有 author="JK Rowling" 的书 两次

【问题讨论】:

  • 对不起,我不认为这很明显。 “合并”两本书是什么意思?为什么会有重复项?
  • 确实可能存在重复,不同的人将书籍添加到数据库中。如果合并书籍,我想确保每个作者只添加一次。
  • 我认为 django ManyToManyField 已经约束了独特的关系。你试过了吗?
  • 我已经尝试过正常的合并,但是如果合并前的两本书有相同的作者,那么作者只是在新合并的书中添加了两次。我尝试了类似models.ManyToManyField(unique=True) 的方法,但我收到消息 ManyToManyFields cannot be unique
  • 您应该解释一下“正常合并”实际上是什么。 显示代码,了解如何合并图书。通常,正如其他人所说,如果项目已经存在,Django 不会将项目添加到 M2M。

标签: python django


【解决方案1】:

Django 将多对多的字段表示为集合。所以你可能想做类似

self.authors = self.authors.union(other_model.authors)

Python sets are collections of unique, unordered objects

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-29
    • 2019-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-12
    • 2020-05-11
    • 2012-11-25
    相关资源
    最近更新 更多