ChatGPT解决这个技术问题 Extra ChatGPT

将 Django 模型对象转换为 dict 并且所有字段都完好无损

如何将 django Model 对象转换为包含所有字段的 dict?理想情况下,所有内容都包括外键和可编辑 = 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: Converting an entire set of a Model's objects into a single dictionary

How can I turn Django Model objects into a dictionary and still have their foreign keys?

您可以声明一个名为 to_dict 的方法并按照您想要的方式处理它。
@karthikr 是的,我可以。问题是如何创建这样的方法。从模型的所有字段手动构建字典不是一个合适的答案。
我会利用现有的 ReST 库,如 Django Rest Framework、Tastypie 或 Piston,因为它们都提供了将 django 模型实例转换为序列化原语的机制。如果您更好奇如何,您可以查看他们的代码,但它主要是遍历模型的 _meta 定义以查找与模型关联的字段并在实例上检索它们的值。

Z
Zags

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

1. 实例.__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_manyforeign_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 是一个字符串而不是一个日期时间对象。

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

如果您想要一个具有更好 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")

现在调用 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}

感谢您的回答!您可以将解决方案 #5(和奖励)中的 isinstance 测试更改为 if f.many_to_many
@dhobbs 我根据 Django 的 model_to_dict 代码对代码进行了建模,该代码使用 isinstance。我不确定他们为什么做出这个选择,但可能有一个很好的理由(例如在以后的版本中引入了 many_to_many 属性)
我想知道这些方法将如何处理带注释/聚合的字段?
我要做的是检查 get_FOO_display 并返回该值,而不是实际可能存在的任何值。
哇,这是我在 StackOverflow 上见过的最好的答案之一。
A
Alfred Huang

我找到了一个巧妙的解决方案来获得结果:

假设您有一个模型对象 o

只需致电:

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

这只是我回答中的选项#4
N
NKSM

最简单的方法,

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


model_dict = Model.Objects.get().__dict__ 提供带有名为 _state 的键的项目以及其余项目
A
A.Raouf

只是 vars(obj) ,它将说明对象的整个值

>>> obj_attrs = vars(obj)
>>> obj_attrs
 {'_file_data_cache': <FileData: Data>,
  '_state': <django.db.models.base.ModelState at 0x7f5c6733bad0>,
  'aggregator_id': 24,
  'amount': 5.0,
  'biller_id': 23,
  'datetime': datetime.datetime(2018, 1, 31, 18, 43, 58, 933277, tzinfo=<UTC>),
  'file_data_id': 797719,
 }

你也可以添加这个

>>> keys = obj_attrs.keys()
>>> temp = [obj_attrs.pop(key) if key.startswith('_') else None for key in keys]
>>> del temp
>>> obj_attrs
   {
    'aggregator_id': 24,
    'amount': 5.0,
    'biller_id': 23,
    'datetime': datetime.datetime(2018, 1, 31, 18, 43, 58, 933277, tzinfo=<UTC>),
    'file_data_id': 797719,
   }

G
Gadget

更新

@zags 发布的更新的汇总答案比我自己的更完整和优雅。请改为参考该答案。

原来的

如果您愿意像@karthiker 建议的那样定义自己的 to_dict 方法,那么这只是将这个问题归结为一个集合问题。

>>># Returns a set of all keys excluding editable = False keys
>>>dict = model_to_dict(instance)
>>>dict

{u'id': 1L, 'reference1': 1L, 'reference2': [1L], 'value': 1}


>>># Returns a set of editable = False keys, misnamed foreign keys, and normal keys
>>>otherDict = SomeModel.objects.filter(id=instance.id).values()[0]
>>>otherDict

{'created': datetime.datetime(2014, 2, 21, 4, 38, 51, tzinfo=<UTC>),
 u'id': 1L,
 'reference1_id': 1L,
 'value': 1L,
 'value2': 2L}

我们需要从 otherDict 中删除错误标记的外键。

为此,我们可以使用一个循环来创建一个新字典,其中包含除下划线之外的所有项目。或者,为了节省时间,我们可以将它们添加到原始字典中,因为字典只是引擎盖下的集合。

>>>for item in otherDict.items():
...    if "_" not in item[0]:
...            dict.update({item[0]:item[1]})
...
>>>

因此,我们留下了以下字典:

>>>dict
{'created': datetime.datetime(2014, 2, 21, 4, 38, 51, tzinfo=<UTC>),
 u'id': 1L,
 'reference1': 1L,
 'reference2': [1L],
 'value': 1,
 'value2': 2L}

而你只是返回。

不利的一面是,您不能在 editable=false 字段名称中使用下划线。从好的方面来说,这将适用于用户自定义字段不包含下划线的任何一组字段。

这不是最好的方法,但它可以作为临时解决方案,直到找到更直接的方法。

对于下面的示例,dict 将基于 model_to_dict 形成,而 otherDict 将由 filter 的 values 方法形成。我会用模型本身来做这件事,但我不能让我的机器接受 otherModel。

>>> import datetime
>>> dict = {u'id': 1, 'reference1': 1, 'reference2': [1], 'value': 1}
>>> otherDict = {'created': datetime.datetime(2014, 2, 21, 4, 38, 51), u'id': 1, 'reference1_id': 1, 'value': 1, 'value2': 2}
>>> for item in otherDict.items():
...     if "_" not in item[0]:
...             dict.update({item[0]:item[1]})
...
>>> dict
{'reference1': 1, 'created': datetime.datetime(2014, 2, 21, 4, 38, 51), 'value2': 2, 'value': 1, 'id': 1, 'reference2': [1]}
>>>

我希望,这应该让您大致了解您的问题的答案。


不确定您在此处尝试使用 re 做什么。如果要过滤掉带有下划线的键,这既不是正确的代码也不是正确的行为。 re.match("_", "reference1_id") 返回 None 并且数据库中的合法列的名称中可能包含下划线。
re.match("_", "reference1_id") 确实返回 None,它应该是:re.match(".*_.*", "reference1_id")
我进行了一些更改以删除坏示例并包含一个更好的示例。我还更改了一些内容以表示这将是所有模型子集的临时解决方案。我不知道您会对 editable=false 字段中带有下划线的模型做什么。我只是想提供一些你可以使用的东西,直到可以提供更多的经典解决方案。
在这种情况下,可能使用 "_" in string 而不是 re
是的,这将是一种更简单的方法。我没有想到以这种方式使用它,但现在它非常有意义。我已将答案更改为使用 in 而不是 re
d
dfcoelho

@Zags 解决方案非常棒!

不过,我会为日期字段添加一个条件,以使其对 JSON 友好。

奖金回合

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

from django.db import models
from django.db.models.fields.related import ManyToManyField

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

    def to_dict(self):
        opts = self._meta
        data = {}
        for f in opts.concrete_fields + opts.many_to_many:
            if isinstance(f, ManyToManyField):
                if self.pk is None:
                    data[f.name] = []
                else:
                    data[f.name] = list(f.value_from_object(self).values_list('pk', flat=True))
            elif isinstance(f, DateTimeField):
                if f.value_from_object(self) is not None:
                    data[f.name] = f.value_from_object(self).timestamp()
            else:
                data[f.name] = None
            else:
                data[f.name] = f.value_from_object(self)
        return data

    class Meta:
        abstract = True

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

class OtherModel(PrintableModel): pass

class SomeModel(PrintableModel):
    value = models.IntegerField()
    value2 = models.IntegerField(editable=False)
    created = models.DateTimeField(auto_now_add=True)
    reference1 = models.ForeignKey(OtherModel, related_name="ref1")
    reference2 = models.ManyToManyField(OtherModel, related_name="ref2")

现在调用 SomeModel.objects.first() 会给出如下输出:

{'created': 1426552454.926738,
'value': 1, 'value2': 2, 'reference1': 1, u'id': 1, 'reference2': [1]}

如果您想在 JSON 之间进行转换,您应该查看 Django Rest Framework 或使用类似这样的东西:stackoverflow.com/a/22238613/2800876
当然!但是对您的代码的这个小改动增加了很多便利!
u
user5359531

更简单的方法是使用基础 Python 中的 pprint

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

这提供了看起来类似于 json.dumps(..., indent = 4) 的输出,但它正确处理了可能嵌入在模型实例中的奇怪数据类型,例如 ModelStateUUID 等。

在 Python 3.7 上测试


E
Enock Simiyu

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

查询集

查询集就像 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)

分页器对象

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

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)

至少我是这样解决的。


b
boatcoder

(没有发表评论的意思)

好的,它实际上并不依赖于那种方式。我可能误解了这里的原始问题,如果是这样,请原谅我。如果您创建 serliazers.py,那么您可以在其中创建具有元类的类。

Class MyModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = modelName
        fields =('csv','of','fields')

然后,当您在视图类中获取数据时,您可以:

model_data - Model.objects.filter(...)
serializer = MyModelSerializer(model_data, many=True)
return Response({'data': serilaizer.data}, status=status.HTTP_200_OK)

这几乎就是我在很多地方所拥有的,它通过 JSONRenderer 返回漂亮的 JSON。

正如我所说,这是由 DjangoRestFramework 提供的,因此值得研究。


M
MD. Khairul Basar

这里有很多有趣的解决方案。我的解决方案是使用 dict 理解向我的模型添加 as_dict 方法。

def as_dict(self):
    return dict((f.name, getattr(self, f.name)) for f in self._meta.fields)

作为奖励,如果您想将模型导出到另一个库,则此解决方案与对查询的列表理解相结合是一个很好的解决方案。例如,将模型转储到 pandas 数据框中:

pandas_awesomeness = pd.DataFrame([m.as_dict() for m in SomeModel.objects.all()])

这适用于字符串和整数等值字段,但外键会出现一些问题,多对多字段会更严重
很好的一点!特别是对于多对多。有人会想要放入一些条件来适当地处理这些情况,或者将此解决方案限制为简单的模型。谢谢。
P
Pjl

也许这对你有帮助。可能这不会隐藏多对多的关系,但是当您想以 json 格式发送模型时非常方便。

def serial_model(modelobj):
  opts = modelobj._meta.fields
  modeldict = model_to_dict(modelobj)
  for m in opts:
    if m.is_relation:
        foreignkey = getattr(modelobj, m.name)
        if foreignkey:
            try:
                modeldict[m.name] = serial_model(foreignkey)
            except:
                pass
  return modeldict

T
Tony

您见过的最佳解决方案。

将 django.db.models.Model 实例和所有相关的 ForeignKey、ManyToManyField 和 @Property 函数字段转换为 dict。

"""
Convert django.db.models.Model instance and all related ForeignKey, ManyToManyField and @property function fields into dict.
Usage:
    class MyDjangoModel(... PrintableModel):
        to_dict_fields = (...)
        to_dict_exclude = (...)
        ...
    a_dict = [inst.to_dict(fields=..., exclude=...) for inst in MyDjangoModel.objects.all()]
"""
import typing

import django.core.exceptions
import django.db.models
import django.forms.models


def get_decorators_dir(cls, exclude: typing.Optional[set]=None) -> set:
    """
    Ref: https://stackoverflow.com/questions/4930414/how-can-i-introspect-properties-and-model-fields-in-django
    :param exclude: set or None
    :param cls:
    :return: a set of decorators
    """
    default_exclude = {"pk", "objects"}
    if not exclude:
        exclude = default_exclude
    else:
        exclude = exclude.union(default_exclude)

    return set([name for name in dir(cls) if name not in exclude and isinstance(getattr(cls, name), property)])


class PrintableModel(django.db.models.Model):

    class Meta:
        abstract = True

    def __repr__(self):
        return str(self.to_dict())

    def to_dict(self, fields: typing.Optional[typing.Iterable]=None, exclude: typing.Optional[typing.Iterable]=None):
        opts = self._meta
        data = {}

        # support fields filters and excludes
        if not fields:
            fields = set()
        else:
            fields = set(fields)
        default_fields = getattr(self, "to_dict_fields", set())
        fields = fields.union(default_fields)

        if not exclude:
            exclude = set()
        else:
            exclude = set(exclude)
        default_exclude = getattr(self, "to_dict_exclude", set())
        exclude = exclude.union(default_exclude)

        # support syntax "field__childField__..."
        self_fields = set()
        child_fields = dict()
        if fields:
            for i in fields:
                splits = i.split("__")
                if len(splits) == 1:
                    self_fields.add(splits[0])
                else:
                    self_fields.add(splits[0])

                    field_name = splits[0]
                    child_fields.setdefault(field_name, set())
                    child_fields[field_name].add("__".join(splits[1:]))

        self_exclude = set()
        child_exclude = dict()
        if exclude:
            for i in exclude:
                splits = i.split("__")
                if len(splits) == 1:
                    self_exclude.add(splits[0])
                else:
                    field_name = splits[0]
                    if field_name not in child_exclude:
                        child_exclude[field_name] = set()
                    child_exclude[field_name].add("__".join(splits[1:]))

        for f in opts.concrete_fields + opts.many_to_many:
            if self_fields and f.name not in self_fields:
                continue
            if self_exclude and f.name in self_exclude:
                continue

            if isinstance(f, django.db.models.ManyToManyField):
                if self.pk is None:
                    data[f.name] = []
                else:
                    result = []
                    m2m_inst = f.value_from_object(self)
                    for obj in m2m_inst:
                        if isinstance(PrintableModel, obj) and hasattr(obj, "to_dict"):
                            d = obj.to_dict(
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name),
                            )
                        else:
                            d = django.forms.models.model_to_dict(
                                obj,
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name)
                            )
                        result.append(d)
                    data[f.name] = result
            elif isinstance(f, django.db.models.ForeignKey):
                if self.pk is None:
                    data[f.name] = []
                else:
                    data[f.name] = None
                    try:
                        foreign_inst = getattr(self, f.name)
                    except django.core.exceptions.ObjectDoesNotExist:
                        pass
                    else:
                        if isinstance(foreign_inst, PrintableModel) and hasattr(foreign_inst, "to_dict"):
                            data[f.name] = foreign_inst.to_dict(
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name)
                            )
                        elif foreign_inst is not None:
                            data[f.name] = django.forms.models.model_to_dict(
                                foreign_inst,
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name),
                            )

            elif isinstance(f, (django.db.models.DateTimeField, django.db.models.DateField)):
                v = f.value_from_object(self)
                if v is not None:
                    data[f.name] = v.isoformat()
                else:
                    data[f.name] = None
            else:
                data[f.name] = f.value_from_object(self)

        # support @property decorator functions
        decorator_names = get_decorators_dir(self.__class__)
        for name in decorator_names:
            if self_fields and name not in self_fields:
                continue
            if self_exclude and name in self_exclude:
                continue

            value = getattr(self, name)
            if isinstance(value, PrintableModel) and hasattr(value, "to_dict"):
                data[name] = value.to_dict(
                    fields=child_fields.get(name),
                    exclude=child_exclude.get(name)
                )
            elif hasattr(value, "_meta"):
                # make sure it is a instance of django.db.models.fields.Field
                data[name] = django.forms.models.model_to_dict(
                    value,
                    fields=child_fields.get(name),
                    exclude=child_exclude.get(name),
                )
            elif isinstance(value, (set, )):
                data[name] = list(value)
            else:
                data[name] = value

        return data

https://gist.github.com/shuge/f543dc2094a3183f69488df2bfb51a52


A
Amin Hemati Nik

@zags 的答案很全面,应该足够了,但是#5 方法(这是 IMO 中最好的方法)会引发错误,所以我改进了辅助函数。

由于 OP 要求将 many_to_many 字段转换为主键列表而不是对象列表,因此我增强了该函数,因此返回值现在是 JSON 可序列化的 - 通过将 datetime 对象转换为 strmany_to_many对象到 id 的列表。

import datetime

def ModelToDict(instance):
    '''
    Returns a dictionary object containing complete field-value pairs of the given instance

    Convertion rules:

        datetime.date --> str
        many_to_many --> list of id's

    '''

    concrete_fields = instance._meta.concrete_fields
    m2m_fields = instance._meta.many_to_many
    data = {}

    for field in concrete_fields:
        key = field.name
        value = field.value_from_object(instance)
        if type(value) == datetime.datetime:
            value = str(field.value_from_object(instance))
        data[key] = value

    for field in m2m_fields:
        key = field.name
        value = field.value_from_object(instance)
        data[key] = [rel.id for rel in value]

    return data

你得到什么错误?我很高兴更新答案
目前通过 concrete_fieldsm2m_fields 的循环的功能看起来相同,因此假设 m2m_fields 循环在此处的实现不正确。
@Zags 错误是 AttributeError: 'list' object has no attribute 'values_list',我找不到它背后的原因。我使用 Django 2.1.1
@daniel-himmelstein 感谢您指出,我修复了代码。相同循环的原因是由于在我的本地代码中执行了不同的操作,而我忘记为 SO 答案优化它。
@ArminHemati Django 改变了 field.value_from_object 的实现,因此改变了 model_to_dict。我已经更新了答案的第 5 节以反映这一点。
L
Lewis

将模型转换为字典并保留所有 ForiegnKey 模型关系。我使用了以下内容:

没有详细名称

from django.forms.models import model_to_dict

instance = MyModel.objects.get(pk=1) # EXAMPLE

instance_dict = {key: getattr(instance, key) for key in model_to_dict(instance).keys()}

输出

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

如果您想在模板中为外键关系显示 __str__() 值,这将很有用。

在 model_to_dict(instance, [...]) 中包含关键字参数 fields= 和 exclude= 使您能够过滤特定字段。

带有详细名称

from django.forms.models import model_to_dict

instance = MyModel.objects.get(pk=1) # EXAMPLE

instance_dict = {instance._meta.get_field(key).verbose_name if hasattr(instance._meta.get_field(key), 'verbose_name') else key: getattr(instance, key) for key in model_to_dict(instance).keys()}

示例输出(如果给定示例有详细名称)

{'Other Model:': [<OtherModel: OtherModel object>],
 'id': 1,
 'My Other Model:': [<OtherModel: OtherModel object>],
 'Normal Value:': 1}

p
pedrobern

我喜欢将模型实例转换为 dict 进行快照测试,我是这样做的:

注意:有骆驼化选项,因为如果 api 响应返回对象 carmelized,最好保持所有快照一致,无论是来自模型实例还是 api 调用。

from rest_framework import serializers
from djangorestframework_camel_case.util import camelize as _camelize

def model_to_dict(instance, camelize=False):
    """
    Convert a model instance to dict.
    """
    class Serializer(serializers.ModelSerializer):
        class Meta:
            model = type(instance)
            fields = "__all__"
    data = Serializer(instance).data
    if camelize:
        data = _camelize(data)
    # convert from ordered dict to dict
    return dict(data)

H
Han Solo

我已经使用下一个函数将模型转换为 dict

def model_to_dict(obj):
    return {x: obj.__dict__[x] for x in obj.__dict__ if x in {y.column for y in obj._meta.fields}}

例子

{'id': 8985,
 'title': 'Dmitro',
 'email_address': 'it9+8985@localhost',
 'workspace_id': 'it9',
 'archived': False,
 'deleted': False,
 'inbox': False,
 'read': True,
 'created_at': datetime.datetime(2022, 5, 5, 16, 55, 29, 791844, tzinfo=    <UTC>),
 'creator': 'An So',
 'last_message_id': 500566,
 'stat_data': {'count_messages': 1, 'count_attachments': 0},
 'stat_dirty': False,
 'assign_to_id': None,
 'assigned_at': None,
 'assignment_note': None,
 'initial_last_update_ts': 1651769728,
 'renamed_manually': False,
 'unread_timestamp': datetime.datetime(2022, 5, 5, 16, 55, 29, 842507, tzinfo=<UTC>)}

{'id': 6670,
 'email_id': 473962,
 'message_id': 500620,
 'filename': 'Screenshot.png',
 'size': 6076854,
 'mimetype': 'image/png',
 'aws_key': 'dev/RLpdcza46KFpITDWO_kv_fg2732waccB43z5RmT9/Screenshot.png',
 'aws_key1': '',
 'aws_key_thumb': 'dev/iaCdvcZmUKq-gJim7HT33ID46Ng4WOdxx-TdVuIU/f4b0db49-7f2d-4def-bdc1-8e394f98727f.png',
 's3stored_file_id': 4147}

s
sascha

我创建了一个利用 django 的 model_to_dict 但遍历对象关系的小 snippet。对于循环依赖,它终止递归并放置一个引用依赖对象的字符串。您可能会扩展它以包含 non_editable 字段。

我在测试期间使用它来创建模型快照。

from itertools import chain

from django.db.models.fields.files import FileField, ImageField
from django.forms.models import model_to_dict


def get_instance_dict(instance, already_passed=frozenset()):
    """Creates a nested dict version of a django model instance
    Follows relationships recursively, circular relationships are terminated by putting
    a model identificator `{model_name}:{instance.id}`.
    Ignores image and file fields."""
    instance_dict = model_to_dict(
        instance,
        fields=[
            f
            for f in instance._meta.concrete_fields
            if not isinstance(f, (ImageField, FileField))
        ],
    )

    already_passed = already_passed.union(
        frozenset((f"{instance.__class__.__name__}:{instance.id}",))
    )
    # Go through possible relationships
    for field in chain(instance._meta.related_objects, instance._meta.concrete_fields):
        if (
            (field.one_to_one or field.many_to_one)
            and hasattr(instance, field.name)
            and (relation := getattr(instance, field.name))
        ):
            if (
                model_id := f"{relation.__class__.__name__}:{relation.id}"
            ) in already_passed:
                instance_dict[field.name] = model_id
            else:
                instance_dict[field.name] = get_instance_dict(relation, already_passed)

        if field.one_to_many or field.many_to_many:
            relations = []
            for relation in getattr(instance, field.get_accessor_name()).all():
                if (
                    model_id := f"{relation.__class__.__name__}:{relation.id}"
                ) in already_passed:
                    relations.append(model_id)
                else:
                    relations.append(get_instance_dict(relation, already_passed))
            instance_dict[field.get_accessor_name()] = relations

    return instance_dict

虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
我认为基本部分存在于文本中(遍历树、终止递归和引用对象),但我确信我可以在此处添加整个片段。