代码之家  ›  专栏  ›  技术社区  ›  David Berger

在Django中插入具有manytomany的对象

  •  1
  • David Berger  · 技术社区  · 16 年前

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

    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获取已经存在的实例并重写它。(远程提交者监听一个feed,该feed将类别对象(除其他外)提供给它进行缓存,因此它可以并且必须通过ID引用这些对象,但它与数据库没有任何直接通信。)

    我真正的问题是:什么是最简单的一致性来膨胀包含了许多管理器的Django模型对象的实例。据我所知,具有多对多字段的对象的任何插入都需要两次数据库命中,这是因为首先需要获得一个新的ID。但我目前的尴尬解决方案是立即保存对象并将其标记为隐藏,这样,下行函数就可以使用它并将其保存为更有意义的内容。似乎一步之遥是压倒性的 save 使没有ID的对象保存一次,将一些代理字段复制到 categories ,然后再次保存。最重要的是,一些健壮的管理器对象可以帮我省去麻烦。你推荐什么?

    2 回复  |  直到 16 年前
        1
  •  2
  •   Jarret Hardie    16 年前

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

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

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

    这种期望在您直接实例化模型的情况下会下降。在这里,您有对模型API的编程访问权,因此您可能会发现直接使用该API比通过通用间接寻址更容易维护,更不容易出错。我可以理解为什么你在想象工厂的概念,但是在这种情况下,你可能会发现为各种模型创建防弹通用化的努力是一个复杂的问题,不值得这样做。

        2
  •  3
  •   S.Lott    16 年前

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

    那又怎么样?

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

    您的应用程序性能通常由缓慢下载到浏览器,以及页面中的所有jpeg、css和其他静态内容控制。

    花在大脑抽筋上的时间思考如何在不进行两次数据库访问的情况下生成两个主键(对于多对多关系而言)是不划算的。两个pk通常是两个数据库访问。


    编辑

    “…出错时丢弃数据库…”

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

    “强制稍后进行验证”

    没有意义——更新你的问题来解释这一点。