将 Django Model 对象转换为 dict,所有字段都完好无损

2024-05-22

如何将 django Model 对象转换为 dictall它的领域?理想情况下,所有内容都包含外键和 editable=False 的字段。

让我详细说明一下。假设我有一个如下所示的 django 模型:

from django.db import models

class OtherModel(models.Model): pass

class SomeModel(models.Model):
    normal_value = models.IntegerField()
    readonly_value = models.IntegerField(editable=False)
    auto_now_add = models.DateTimeField(auto_now_add=True)
    foreign_key = models.ForeignKey(OtherModel, related_name="ref1")
    many_to_many = models.ManyToManyField(OtherModel, related_name="ref2")

在终端中,我做了以下操作:

other_model = OtherModel()
other_model.save()
instance = SomeModel()
instance.normal_value = 1
instance.readonly_value = 2
instance.foreign_key = other_model
instance.save()
instance.many_to_many.add(other_model)
instance.save()

我想将其转换为以下字典:

{'auto_now_add': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
 'foreign_key': 1,
 'id': 1,
 'many_to_many': [1],
 'normal_value': 1,
 'readonly_value': 2}

答案不满意的问题:

Django:将整个模型对象集转换为单个字典 https://stackoverflow.com/questions/1123337/django-converting-an-entire-set-of-a-models-objects-into-a-single-dictionary

如何将 Django Model 对象转换为字典并仍然保留其外键? https://stackoverflow.com/questions/12382546/how-can-i-turn-django-model-objects-into-a-dictionary-and-still-have-their-forei


将实例转换为字典的方法有很多,不同程度的极端情况处理和与所需结果的接近程度不同。


1. instance.__dict__

instance.__dict__

返回

{'_foreign_key_cache': <OtherModel: OtherModel object>,
 '_state': <django.db.models.base.ModelState at 0x7ff0993f6908>,
 'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key_id': 2,
 'id': 1,
 'normal_value': 1,
 'readonly_value': 2}

这是迄今为止最简单的,但缺少many_to_many, foreign_key命名错误,并且其中有两个不需要的额外内容。


2. model_to_dict

from django.forms.models import model_to_dict
model_to_dict(instance)

返回

{'foreign_key': 2,
 'id': 1,
 'many_to_many': [<OtherModel: OtherModel object>],
 'normal_value': 1}

这是唯一一个带有many_to_many,但缺少不可编辑的字段。


3. model_to_dict(..., fields=...)

from django.forms.models import model_to_dict
model_to_dict(instance, fields=[field.name for field in instance._meta.fields])

返回

{'foreign_key': 2, 'id': 1, 'normal_value': 1}

这比标准严重糟糕model_to_dict调用。


4. query_set.values()

SomeModel.objects.filter(id=instance.id).values()[0]

返回

{'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key_id': 2,
 'id': 1,
 'normal_value': 1,
 'readonly_value': 2}

这与输出相同instance.__dict__但没有额外的字段。foreign_key_id仍然是错误的并且many_to_many仍然失踪。


5. 自定义功能

django 的代码model_to_dict得到了大部分答案。它明确删除了不可编辑的字段,因此删除该检查并获取多对多字段的外键 ID 会产生以下代码,其行为符合预期:

from itertools import chain

def to_dict(instance):
    opts = instance._meta
    data = {}
    for f in chain(opts.concrete_fields, opts.private_fields):
        data[f.name] = f.value_from_object(instance)
    for f in opts.many_to_many:
        data[f.name] = [i.id for i in f.value_from_object(instance)]
    return data

虽然这是最复杂的选项,但调用to_dict(instance)给了我们完全想要的结果:

{'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key': 2,
 'id': 1,
 'many_to_many': [2],
 'normal_value': 1,
 'readonly_value': 2}

6. 使用序列化器

Django 休息框架 https://www.django-rest-framework.org/的 ModelSerializer 允许您从模型自动构建序列化器。

from rest_framework import serializers
class SomeModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = SomeModel
        fields = "__all__"

SomeModelSerializer(instance).data

returns

{'auto_now_add': '2018-12-20T21:34:29.494827Z',
 'foreign_key': 2,
 'id': 1,
 'many_to_many': [2],
 'normal_value': 1,
 'readonly_value': 2}

这几乎与自定义函数一样好,但 auto_now_add 是一个字符串而不是日期时间对象。


奖励回合:更好的模型打印

如果您想要一个具有更好的 python 命令行显示的 django 模型,请让您的模型子类具有以下内容:

from django.db import models
from itertools import chain

class PrintableModel(models.Model):
    def __repr__(self):
        return str(self.to_dict())

    def to_dict(instance):
        opts = instance._meta
        data = {}
        for f in chain(opts.concrete_fields, opts.private_fields):
            data[f.name] = f.value_from_object(instance)
        for f in opts.many_to_many:
            data[f.name] = [i.id for i in f.value_from_object(instance)]
        return data

    class Meta:
        abstract = True

因此,例如,如果我们这样定义我们的模型:

class OtherModel(PrintableModel): pass

class SomeModel(PrintableModel):
    normal_value = models.IntegerField()
    readonly_value = models.IntegerField(editable=False)
    auto_now_add = models.DateTimeField(auto_now_add=True)
    foreign_key = models.ForeignKey(OtherModel, related_name="ref1")
    many_to_many = models.ManyToManyField(OtherModel, related_name="ref2")

Calling SomeModel.objects.first()现在给出这样的输出:

{'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key': 2,
 'id': 1,
 'many_to_many': [2],
 'normal_value': 1,
 'readonly_value': 2}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将 Django Model 对象转换为 dict,所有字段都完好无损 的相关文章

随机推荐