如何在 django 中处理未保存的多对多关系?

Posted

技术标签:

【中文标题】如何在 django 中处理未保存的多对多关系?【英文标题】:How to work with unsaved many-to-many relations in django? 【发布时间】:2010-10-05 19:30:34 【问题描述】:

我在 django 中有几个模型,它们是多对多连接的。我想在内存中创建这些模型的实例,将它们呈现给用户(通过视图模板中的自定义方法调用),如果用户满意,将它们保存到数据库中。 p>

但是,如果我尝试对模型实例执行任何操作(例如调用渲染方法),我会收到一条错误消息,提示我必须先保存实例。文档说这是因为模型处于多对多关系。

我如何向用户展示对象并允许他/她保存或丢弃它们而不会使我的数据库混乱?

(我想我可以关闭事务处理并在整个项目中自己进行,但这听起来可能是一个容易出错的措施......)

谢谢!

【问题讨论】:

【参考方案1】:

我会添加一个字段来指示对象是“草稿”还是“活动”。这样它们就会在请求、会话等中持续存在,并且 django 停止抱怨。

然后您可以过滤您的对象以仅在公共视图中显示“活动”对象,并且仅向创建它们的用户显示“草稿”对象。这也可以扩展为允许“归档”对象(或任何其他有意义的状态)。

【讨论】:

【参考方案2】:

我认为使用 django 表单可能是答案,如 this 文档中所述(搜索 m2m...)。

为可能遇到相同问题的其他人添加了一些解释:

假设你有这样的模型:

from django.db import models
from django.forms import ModelForm

class Foo(models.Model):
    name = models.CharField(max_length = 30)

class Bar(models.Model):
      foos = models.ManyToManyField(Foo)

  def __unicode__(self):
      return " ".join([x.name for x in foos])

那么您不能在未保存的 Bar 对象上调用 unicode()。如果您确实想在保存之前打印出来,您必须这样做:

class BarForm(ModelForm):
    class Meta:
        model = Bar

def example():      
    f1 = Foo(name = 'sue')
    f1.save()
    f2 = foo(name = 'wendy')
    f2.save()
    bf = BarForm('foos' : [f1.id, f2.id])
    b = bf.save(commit = false)
    # unfortunately, unicode(b) doesn't work before it is saved properly,
    # so we need to do it this way: 
    if(not bf.is_valid()):
        print bf.errors
    else:
        for (key, value) in bf.cleaned_data.items():
            print key + " => " + str(value)

因此,在这种情况下,您必须保存 Foo 对象(您可以在保存这些对象之前使用它们自己的形式对其进行验证),并且在保存具有多对多键的模型之前,您也可以验证它们。所有这些都不需要过早保存数据并弄乱数据库或处理事务......

【讨论】:

【参考方案3】:

答案很晚,但是 wagtail 的团队制作了一个单独的 Django 扩展,名为 django-modelcluster。这就是他们 CMS 的草稿预览的动力。

它允许你做这样的事情(来自他们的自述文件):

from modelcluster.models import ClusterableModel
from modelcluster.fields import ParentalKey

class Band(ClusterableModel):
    name = models.CharField(max_length=255)

class BandMember(models.Model):
    band = ParentalKey('Band', related_name='members')
    name = models.CharField(max_length=255)

那么模型可以像这样使用:

beatles = Band(name='The Beatles')
beatles.members = [
    BandMember(name='John Lennon'),
    BandMember(name='Paul McCartney'),
]

这里,ParentalKey 是 Django 的 ForeignKey 的替代品。同样,他们有ParentalManyToManyField 来替换Django 的ManyToManyField

【讨论】:

以上是关于如何在 django 中处理未保存的多对多关系?的主要内容,如果未能解决你的问题,请参考以下文章

如何从django orm中的多对多关系表中提取数据

如何保存具有直通关系的多对多字段

在 django 的多对多关系中添加额外的字段

将 Django 中的多对多关系表示为两个多项选择

从 Django 查询中的多对多关系中检索项目

根据特定的多对多关系过滤 Django 查询集