DRF:休息对象内的内部关系,表示为SlugRelatedFIelds。不可能?

时间:2014-08-15 16:49:41

标签: django django-rest-framework

假设我有三个相关模型 - 容器,类别和项目。每个模型都有两个字符串属性 - 名称和uuid(以及普通的“id”字段).uuid字段将用作SlugRelatedFields来表示REST接口中的关系。容器包含一组项目和一组类别;每个项目都与一个类别相关。

我想在一个POST中创建一个完整的容器(包含所有项目和类别)。但我似乎无法创建一个允许它的序列化器。当我尝试这样做时,当序列化程序尝试解码项时我收到错误 - 它发现引用的类别尚不存在。这并不奇怪,因为相同的POST将创建它。

以JSON形式,这是我想发布的内容:

{
    "name": "My Container",
    "uuid": "<container-uuid>",
    "categories": [
        {
            "name": "Category One",
            "uuid": "<category-one-uuid>",
        }
    ],
    "items": [
        {
            "name": "Item One",
            "uuid": "<item-one-uuid>",
            "category": "<category-one-uuid>",
        },
        {
            "name": "Item Two",
            "uuid": "<item-two-uuid>",
            "category": "<category-one-uuid>",
        }
    ]
}

因此,该示例将创建一个容器,一个类别和两个项目 - 这两个项目将与同一类别相关。

我可以在Item序列化程序中使用SlugRelatedField来通过uuid标识关联的Category。但在这种情况下,会引发错误:

Object with uuid=<category-one-uuid> does not exist.

反序列化“第一项”。可以理解,因为该类别尚未出现在数据库中。

有没有办法让这项工作?我可以覆盖ItemSerializer中的某个方法,使其查找正在反序列化的Container中的关联类别,而不是查看数据库中已存在的类别吗?我认为解决方案可能需要一些东西:

  • 确保在项目之前对类别进行反序列化。是什么决定了?序列化程序中“fields”属性的顺序?
  • 使用反序列化的类别创建在反序列化项目期间使用的查询集,替换通常使用的数据库查询。

有什么想法吗?

更新2014-08-22:

我没有解决这个问题,但我已经解决了这个问题。解决方法是一个肮脏的黑客,涉及许多部分:

  • 在模型中,我更改了外键引用的定义 从项目到类别允许NULL。

  • 在Item的序列化程序中,我 将类别声明为一个简单的CharField(),而不是一个 SlugRelatedField通过其UUID标识类别。

  • 在该序列化程序的“restore_object”方法中,我从类别字段中获取指定的UUID(在传递给该方法的“attrs”字典中)。我从该字典中删除该条目,并将一个条目添加到将Item UUID映射到关联的Category UUID的字典中​​。该字典保存在每个请求缓存中,如下所述:Per-request cache in Django?

  • 然后我在item上有一个“pre_save”信号的处理程序,它在该字典中查找所需的类别UUID(使用Items的UUID作为键),在Category表上查询以查找正确的项目并使用它来设置foreignkey字段。

因此,当请求进入以创建具有关联项目和类别的新Container时:

  • 由于Item中的Category UUID引用只是CharField,因此不会根据对数据库中现有类别的查询进行验证。因此序列化程序继续在数据库中保存内容。
  • 类别会照常保存到数据库中。 (他们先被保存,大概只是因为Seriliazer的“田野”阵列的顺序。)
  • 当使用“restore_object”方法构造将要保存的Item时,所需类别的UUID将被隐藏起来供以后使用,并且从用于构造Item的字段中丢弃“CharField”值。
  • 当要保存项目时,信号处理程序检索隐藏的UUID并使用它来获取正确的(新创建的)类别以填写foreignkey字段。
哈克,好吧?但似乎有效。我希望有一天能够使用更优雅的解决方案进行返工,但在现实世界中,这种情况不会发生。

1 个答案:

答案 0 :(得分:0)

这是我的想法:

  • 转到 rest_framework / serializers.py
  • ModelSerializer 类中查找 save_object 方法
  • 在那里,您应该找到一段像这样的代码:

        if getattr(obj, '_nested_forward_relations', None):
        # Nested relationships need to be saved before we can save the
        # parent instance.
        for field_name, sub_object in obj._nested_forward_relations.items():
            if sub_object:
                self.save_object(sub_object)
            setattr(obj, field_name, sub_object)
    

我最好的猜测是,序列化程序会尝试保存嵌套对象(类别和项目)。

  • 在那里放置一个断点并试图找出这些对象的确切创建方式(按什么顺序)
  • 如果您发现任何有趣的内容,可以更新您的帖子,我会尽力帮助您。
祝你好运!