【问题标题】:How to override foreign key null values in django serializer?如何在 Django 序列化程序中覆盖外键空值?
【发布时间】:2021-05-03 16:29:05
【问题描述】:

我正在使用 natural_keys 将查询集序列化为 json 格式。参考:docs

我能够成功地序列化数据。如果有任何外键,那么我也可以添加它的对象而不是外键。例如:

class Parent(models.Model):
    name = models.CharField()

    def get_natural_keys(self):
        return(
                 {'name': self.name, 'pk': self.pk}
        )

class Child(models.Model):
    name = models.CharField()
    parent = models.ForeignKey(Parent, null=True)

在查询数据时:

child = serializers.serialize('json', list(Child.objects.all()), user_natural_foreign_keys=True, use_natural_primary_keys=True)

这将返回 json:

{
    "model": 'proj.child'
    "pk": 1,
    "fields": {
                "name": "child name",
                "parent": {"id": 1, "name": "parent name"}
              }
}

到目前为止,一切都很好。我的问题是,当父外键在子中为空时,它在父中返回 None:

fields: {
    "name": "child name",
    "parent": None
}

我的期望是:

fields: {
    "name": "child name",
    "parent": {"id": None. "name": None}
}

如何将 None 值覆盖到另一个字典? 一种方法是遍历字典列表并对其进行编辑。但是,我觉得它不是最好的。

[编辑]

为了让我的设计更具体:

class Person(models.Model):
    name = models.CharField()
    phone = models.CharField()
 
class Building(modls.Model):
    name = models.CharField()
    address = models.CharField()
    build_by = models.ForeignKey(Person, null=False)
    owner = models.ForeignKey(Person)
    residing_by = models.ForeignKey(Person, null=True)

首先,我尝试序列化building 对象,它在序列化数据中有foreign keys。但是,我没想到序列化数据中有外键。相反,我想要另一个序列化数据来代替外键。所以,我遇到了 get_natural_keys() ,我可以使用它来序列化外键对象。我可以自定义get_natural_keys()返回一个序列化的数据。

在上述建筑模型中,residing_by 在某个时间点可能为空。对于序列化数据中的 null 值,我想用另一个字典(如 {'id': None, 'name': None})覆盖 null。

【问题讨论】:

  • 如果你需要高级模型序列化我建议你使用 django restframework 序列化器
  • 我建议不要这样做。你的父母不存在。您没有没有身份证和姓名的父母。区别很重要,尤其是当更多字段发挥作用时,甚至是嵌套外键。任何消耗序列化数据的东西都应该能够处理。
  • @Melvyn,我尝试在这里复制我的问题。实际上,parent_id 存在于父表中。这是子表不愿意引用父表的场景。所以,我不得不为子表中的父引用保留 null 。如果这也不是一个好方法,那么建议一个更好的方法。

标签: python django serialization


【解决方案1】:

您的设置存在一些问题:

  • 自然键的全部意义在于避免自动生成数据,例如自动递增主键,以便您可以识别其他数据库(生产、暂存)中可能具有不同插入顺序的记录。相反,您将 自动生成的 主键作为 自然键 返回。
  • 您似乎希望将序列化框架用于它不适合的东西,或者其他包(如 Django REST Framework)做得更好。
  • 您的模型不适合自然键,因为它们只有一个字段并且它不是唯一的,因此如果不使用主键就无法引用记录。
  • 最后,我不明白为什么你需要自然键开始。是什么让您决定这样做?

这是子表不愿意引用父表的场景

我不确定这意味着什么。您将孩子链接到父母,或者您没有。他们不是真正的孩子,应该服从你的程序:)。如果需要父级,则 not 将 null=True 添加到外键中,这样它就会抛出错误,然后你就知道你的编程问题出在哪里了。

总而言之,我认为您对事情的运作方式以及如何解决这些问题做了一些假设,但您选择的解决方案并不合适。

如前所述,您应该首先弄清楚为什么可以在没有父母的情况下创建孩子,如果这不是您想要的并解决这个问题。然后重新评估序列化应该如何工作,因为在自然键中粘贴自动 ID 是没有意义的。您可能不需要自然键。如果您这样做是为了更改输出格式,那么正如其他人所建议的那样,DRF 为您提供了更好的选择,但它也伴随着陡峭的学习曲线。

【讨论】:

  • 这是有用的信息。我已经更新了我的问题。我想我在使用自然键序列化外键数据方面采取了错误的举措。
【解决方案2】:

当我遇到类似的问题时,类似的方法有效:

class Chield(models.Model):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if not self.parent:
            self.parent = Parent()
    name = models.CharField()
    parent = models.ForeignKey(Parent, null=True)

【讨论】:

  • 是的,这是我正在寻找的东西。我会考虑这个答案,因为我不需要更改我的大部分代码。当我获取更多数据时,这种方法会增加时间复杂度吗?因为,我假设条件语句会触发数据库中的每个条目。我说的对吗?
【解决方案3】:

对于此类用例,我建议使用 django-rest-framework serializers


from .models import Parent, Child

from rest_framework import serializers

### Define Serializers

class ParentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Parent
        fields = ['id', 'name']
        
class ChildSerializer(serializers.ModelSerializer):
    parent = ParentSerializer()
    
    class Meta:
        model = Child
        fields = ['id', 'name', 'parent']
        
   def to_representation(self, instance):
        # get representation from ModelSerializer
        ret = super(ChildSerializer, self).to_representation(instance)
        # if parent is None, overwrite
        if not ret.get("parent", None):
            ret["parent"] = {"id": None, "name": None}
        return ret
        
        
### example serialization

childs = ChildSerializer(Child.objects.all(), many=True)

print(childs.data)

"""
Output:
[
    {
        "id": 1,
        "name": "example child name",
        "parent": {
            "id": 1,
            "name": "example parent name"
        }
    },
    #...snip..
]
"""

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-19
    • 2021-05-09
    • 1970-01-01
    • 1970-01-01
    • 2021-03-19
    • 1970-01-01
    • 2023-03-17
    相关资源
    最近更新 更多