如何在Django中更新manytomany字段?

Eti*_*nne 16 python django

这是一个例子:

如果我有这些课程

class Author(models.Model):
    name = models.CharField(max_length=45)

class Book(models.Model):
    name = models.CharField(max_length=45)
    authors = models.ManyToManyField(Author)
Run Code Online (Sandbox Code Playgroud)

在数据库中,我有一个名为"George"的作者和另一个名为"Georfe"的作者.最后一个是错误的.所以我想要的是每本书都有"Georfe"作为他的作者之一取代作者"乔治".

在SQL中真的很容易做到.如果"George"的id = 3且"Georfe"的id = 7且关系表名称为"author_book":

UPDATE author_book SET id=3 WHERE id=7;
Run Code Online (Sandbox Code Playgroud)

用Django ORM可以做到这一点吗?

我找到了一种方法:我通过错误的作者的所有相关书籍循环,并做:

book.authors.add(Author.objects.get(id=3))
book.authors.remove(Author.objects.get(id=7))
Run Code Online (Sandbox Code Playgroud)

但我发现这种解决方案并不优雅高效.有没有循环的解决方案?

Ian*_*and 20

注意:此代码将删除错误的"georfe"作者,以及更新书籍以指向正确的作者.如果您不想这样做,请使用.remove()@jcdyer的答案提及.

你能做这样的事吗?

george_author = Author.objects.get(name="George")
for book in Book.objects.filter(authors__name="Georfe"):
    book.authors.add(george_author.id)
    book.authors.filter(name="Georfe").delete()
Run Code Online (Sandbox Code Playgroud)

我怀疑如果你有一个明确的表连接两个模型(使用"through"关键字arg)会更容易 - 在这种情况下,你可以直接访问关系表,并且可以只.update(id=george_author.id)对它进行操作.

  • 使用`.filter(...).delete()`实际上删除了作者,而不仅仅是多对多关系.jcdyer有正确的答案:调用`remove()`. (3认同)

jcd*_*yer 17

使用自动生成的直通表,您可以执行两步插入和删除操作,这对于提高可读性非常有用.

george = Author.objects.get(name='George')
georfe = Author.objects.get(name='Georfe')

book.authors.add(george)
book.authors.remove(georfe)
assert george in book.authors
Run Code Online (Sandbox Code Playgroud)

如果你有一个明确定义的直通表(authors = models.ManyToManyField(作者,通过= BookAuthors),那么你可以在BookAuthor上明确地改变关系.一个鲜为人知的事实是这个模型已经存在,它是由django自动生成的.通常如果您希望存储额外的数据(例如特定作者在多作者书中写过的章节),您应该只创建一个显式的直通模型.

# This line is only needed without a custom through model.
BookAuthor = Book.authors.through
book_author = BookAuthor.objects.get(author=georfe, book=great_american_novel)
book_author.author = george
book_author.save()
assert george in book.authors
Run Code Online (Sandbox Code Playgroud)


小智 10

对于 Django >=1.11 文档:

>>> b = Blog.objects.get(id=1)
>>> e = Entry.objects.get(id=234)
>>> b.entry_set.add(e) # Associates Entry e with Blog b.
>>> new_list = [obj1, obj2, obj3]
>>> e.related_set.set(new_list)
Run Code Online (Sandbox Code Playgroud)

此方法接受一个clear参数来控制如何执行操作。如果False(默认),新集合中缺少的元素将被删除,remove()并且只添加新元素。如果clear=Trueclear()则调用该方法并立即添加整个集合。

和参考:如何在 django 中更新 m2m 字段