【问题标题】:Inserting object with ManyToMany in Django在 Django 中使用 ManyToMany 插入对象
【发布时间】:2009-04-07 18:16:01
【问题描述】:

我有一个类似博客的应用程序,其中包含故事和类别:

class Category(models.Model):
    ...
class Story(models.Model):
    categories = models.ManyToManyField(Category)
    ...

现在我知道,当您保存具有多对多字段的模型的新实例时,会出现问题,因为该对象尚未在数据库中。这个问题通常表现在表单提交上,可以通过story_form.save(commit=False) 巧妙地解决。没有形式可言的情况呢?就我而言,我想构建一个 API 来接受远程提交。由于我喜欢 JSON,而且我们公司的很多其他消息都是 JSON 格式的(包括来自该服务器的传出消息),我希望能够接收以下内容:

{ "operation": "INSERT",
  "values": [
            { "datatype": "story",
              "categories": [4,6,8],
              "id":50,
              ...
            }
            ]
}

并实现一个将值转换为实例的工厂。但我希望工厂对操作类型尽可能不可知。所以:

{ "operation": "UPDATE",
  "values": [
            { "datatype": "story",
              "categories": [4,6,8],
              "id":50,
              ...
            }
            ]
}

也应该以相同的方式进行转换,除了 INSERT 忽略 id 并且 UPDATE 获取已经存在的实例并覆盖它。 (远程提交者监听一个提要,除其他外,它提供了要缓存的类别对象,因此它可以而且必须通过 id 引用它们,但它与数据库没有任何直接通信。)

我真正的问题是:对涉及 ManyToManyManager 的 Django 模型对象的实例进行膨胀最简单的方法是什么。据我所知,任何插入具有多对多字段的对象都需要两次数据库命中,因为首先需要获得一个新的 id。但我目前尴尬的解决方案是立即保存对象并将其标记为隐藏,以便后续功能可以使用它并将其保存为更有意义的东西。似乎上一步将覆盖save,以便没有ID的对象保存一次,将一些代理字段复制到categories,然后再次保存。最重要的是一些强大的管理器对象可以为我省去麻烦。你有什么推荐的?

【问题讨论】:

    标签: python django many-to-many


    【解决方案1】:

    “据我所知,任何插入具有多对多字段的对象都需要两次数据库命中,...”

    那又怎样?

    一般来说,对每个单独的数据库访问进行微观管理并不值得考虑。做最简单、最明显的事情,以便 Django 可以为您优化缓存。

    您的应用程序性能——通常——主要由下载到浏览器的速度慢以及页面中的所有 JPEGS、CSS 和其他静态内容支配。

    花在思考如何在不进行两次数据库访问的情况下创建两个主键(对于多对多关系)上的时间不会得到很好的回报。两个 PK 通常是两个数据库访问。


    编辑

    “...在错误时乱扔数据库...”

    Django 有事务。见http://docs.djangoproject.com/en/dev/topics/db/transactions/#managing-database-transactions。使用 @transaction.commit_manually 装饰器。

    “强制稍后进行的验证”

    没有意义 - 更新您的问题以解释这一点。

    【讨论】:

    • +1 表示在“脑筋急转弯”这一短语中工作。不过,严肃地说,+1 指出对数据库进行微观管理可能不值得。在这种情况下,使用 Django 的 ORM 带来了太多好处,无需担心细节。
    • 我同意担心两次数据库命中是不值得的。 (我什至不认为我可以用原始 SQL 做得更好。)但我不喜欢为了让我的实现工作而不得不提前保存。它会在数据库中乱扔错误,并强制进行稍后进行的验证。
    【解决方案2】:

    我评论了 S.Lott 的帖子,我觉得他的回答是最好的。他是对的:如果目标只是避免两次数据库命中,那么您将陷入一个不必要的痛苦世界。

    阅读您对 ModelForm 的引用,但是,如果您正在寻找一种解决方案,允许您以某种方式推迟正式保存,您可能希望查看 forms.models 中的 save_instance() 函数。内部函数save_m2m 是如何为表单完成延迟多对多保存。为没有表单的模型实现一些东西基本上遵循相同的原则。

    话虽如此,回到 S.Lott 的帖子,ModelForm 和实际 Model 的情况有些不同。因为表单只公开了要在浏览器中编辑的“安全”数据集(“安全”是因为它以某种方式被过滤,或者排除了用户不应该编辑的关键字段),所以合理的设计期望是有人可能需要在保存之前向表单派生模型添加重要信息。这就是 django 有 commit=False 的原因。

    对于直接实例化模型的情况,这种期望会下降。在这里,您可以对模型 API 进行编程访问,因此您可能会发现直接使用该 API 比通过通用间接方式更易于维护且不易出错。我可以理解您为什么要描绘工厂概念,但在这种情况下,您可能会发现为各种模型创建防弹泛化的努力是不值得的。

    【讨论】:

    • 我或多或少喜欢这个。我仍然认为在保存之前您无法访问 m2m 字段是无稽之谈。预期的行为很明显。
    猜你喜欢
    • 2016-11-18
    • 1970-01-01
    • 1970-01-01
    • 2010-11-24
    • 1970-01-01
    • 2016-01-11
    • 2021-10-30
    • 1970-01-01
    • 2013-05-19
    相关资源
    最近更新 更多