如何覆盖 Django 序列化程序中的外键空值?

How to override foreign key null values in django serializer?

我正在使用 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)

这将 return json:

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

到目前为止一切都很好。我的问题是,当父级外键在子级中为 null 时,它在父级中 returns 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。但是,我没想到序列化数据中有外键。相反,我想要另一个序列化数据来代替 foreign key.So,我遇到了 get_natural_keys() 使用它我可以序列化外键对象。我可以自定义get_natural_keys()到return一个序列化数据。

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

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

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)

对于此类用例,我建议使用 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..
]
"""

您的设置存在一些问题:

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

This is the scenario where child table is not willing to refer for parent table

我不确定那是什么意思。你 link child 到 parent 或者你不。它们不是真正的 children,应该服从您的编程:)。如果需要parent,那么not 将null=True 添加到外键,这样它就会抛出一个错误,然后你就知道你的编程问题在哪里了。

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

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