如何在Django序列化程序中重写外键空值?

2024-04-29 10:38:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在使用自然键将查询集序列化为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"}
              }
}

到目前为止,一切都很好。我的问题是,当父外键在子项中为null时,它在父项中不返回任何值:

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()以返回序列化数据

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


Tags: 数据namenonechildmodel序列化modelskeys
3条回答

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

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..
]
"""

您的设置存在一些问题:

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

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

我不知道那是什么意思。您可以将子对象链接到父对象,也可以不链接。他们不是真正的孩子,应该遵守你的计划:)。如果需要父项,则对外键执行notadd null=True,这样它就会抛出一个错误,然后您就知道编程问题出在哪里了

总而言之,我认为你对事情如何运作以及如何解决它们做出了一些假设,但你选择的解决方案并不合适

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

相关问题 更多 >