如何将django Model对象转换为包含所有字段的dict ?理想情况下,所有字段都包含外键和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:将整个Model对象集转换为单个字典

如何将Django Model对象转换为字典,同时还保留外键?


当前回答

当我试图使用django-rest框架将django站点转换为API时,我遇到了这个问题。通常django会从数据库中返回三种类型的对象。它们包括一个查询集、一个模型实例和一个分页器对象。对我来说,这些是需要转换的。

查询集

queryset就像django中的模型对象列表。这是把它转换成字典的代码。

model_data=Model.object.all()# This returns a queryset object
model_to_dict=[model for model in model_data.values()]
return Response(model_to_dict,status=status.HTTP_200_OK)

模型实例

模型实例是模型的单个对象。

model_instance=Model.objects.get(pk=1)# This will return only a single model object
model_to_dict=model_to_dict(model_instance)
return Response(model_to_dict,status=status.HTTP_200_OK)

Paginator对象

分页器对象是一个包含特定页面的模型对象的对象。

model_queryset=Model.objects.all()
paginator = Paginator(model_queryset, 10)
try:
    selected_results = paginator.page(page)
except Exception:
    selected_results=result
paginator_to_dict=list(selected_results.object_list.values())
return Response(selected_results,status=status.HTTP_200_OK)

至少我是这么解决的。

其他回答

当我试图使用django-rest框架将django站点转换为API时,我遇到了这个问题。通常django会从数据库中返回三种类型的对象。它们包括一个查询集、一个模型实例和一个分页器对象。对我来说,这些是需要转换的。

查询集

queryset就像django中的模型对象列表。这是把它转换成字典的代码。

model_data=Model.object.all()# This returns a queryset object
model_to_dict=[model for model in model_data.values()]
return Response(model_to_dict,status=status.HTTP_200_OK)

模型实例

模型实例是模型的单个对象。

model_instance=Model.objects.get(pk=1)# This will return only a single model object
model_to_dict=model_to_dict(model_instance)
return Response(model_to_dict,status=status.HTTP_200_OK)

Paginator对象

分页器对象是一个包含特定页面的模型对象的对象。

model_queryset=Model.objects.all()
paginator = Paginator(model_queryset, 10)
try:
    selected_results = paginator.page(page)
except Exception:
    selected_results=result
paginator_to_dict=list(selected_results.object_list.values())
return Response(selected_results,status=status.HTTP_200_OK)

至少我是这么解决的。

我找到了一个很好的解决方法:

假设你有一个模型对象o:

就叫:

type(o).objects.filter(pk=o.pk).values().first()

有很多种方法可以将实例转换为字典,这些方法对极端情况的处理程度各不相同,与预期结果的接近程度也各不相同。


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(…、字段=…)

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 Rest Framework的ModelSerializer允许你从一个模型自动构建一个序列化器。

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

SomeModelSerializer(instance).data

返回

{'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是一个字符串,而不是一个datetime对象。


奖励轮:更好的模型印刷

如果你想要一个更好的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")

调用sommodel .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}

最简单的方式,

如果你的查询是Model.Objects.get(): Get()将返回单个实例,因此您可以直接从实例中使用__dict__。 model_dict = Model.Objects.get().__dict__ 过滤器()/ (): All ()/filter()将返回实例列表,因此您可以使用values()来获取对象列表。 model_values = Model.Objects.all().values()

更简单的方法是使用pprint,它是基于Python的

import pprint
item = MyDjangoModel.objects.get(name = 'foo')
pprint.pprint(item.__dict__, indent = 4)

这将提供类似于json.dumps(…, indent = 4),但它正确地处理了可能嵌入在模型实例中的奇怪的数据类型,如ModelState和UUID等。

在Python 3.7上测试