如何将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: 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?
答案 0 :(得分:349)
有很多方法可以将实例转换为字典,具有不同程度的角落案例处理和与所需结果的接近程度。
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
被错误命名,并且它有两个不需要的额外内容。
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
,但缺少不可编辑的字段。
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
调用更糟糕。
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
仍然缺失。
django model_to_dict
的代码有大部分答案。它明确删除了不可编辑的字段,因此删除该检查会产生以下代码,其行为符合要求:
from django.db.models.fields.related import ManyToManyField
def to_dict(instance):
opts = instance._meta
data = {}
for f in opts.concrete_fields + opts.many_to_many:
if isinstance(f, ManyToManyField):
if instance.pk is None:
data[f.name] = []
else:
data[f.name] = list(f.value_from_object(instance).values_list('pk', flat=True))
else:
data[f.name] = 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}
Django Rest Framework的ModelSerialzer允许您从模型中自动构建序列化程序。
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模型,请让你的模型为child-class:
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))
else:
data[f.name] = f.value_from_object(self)
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}
答案 1 :(得分:9)
我找到了一个简洁的解决方案来获得结果:
假设您有一个模型对象o
:
请致电:
type(o).objects.filter(pk=o.pk).values().first()
答案 2 :(得分:5)
@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]}
答案 3 :(得分:3)
最简单的方法,
如果您的查询是 Model.Objects.get():
get()将返回单个实例,因此您可以直接使用实例
中的__dict__
model_dict = Model.Objects.get().__dict__
filter()/ all():
all()/ filter()将返回实例列表,以便您可以使用values()
获取对象列表。
model_values = Model.Objects.all()。values()
答案 4 :(得分:2)
如果您愿意定义自己的to_dict方法,例如@karthiker建议,那么这只会将此问题归结为集合问题。
>>># 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 中删除标记错误的外键。
要做到这一点,我们可以使用一个循环来创建一个新的字典,除了那些带有下划线的项目之外。或者,为了节省时间,我们可以将它们添加到原始的 dict ,因为词典只是在幕后设置。
>>>for item in otherDict.items():
... if "_" not in item[0]:
... dict.update({item[0]:item[1]})
...
>>>
因此,我们留下以下 dict :
>>>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]}
>>>
这应该让你对你的问题的答案进行粗略的讨论,我希望。
答案 5 :(得分:2)
只是vars(obj)
,它将说明对象的整个值
{'_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,
}
答案 6 :(得分:2)
当我尝试使用 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)
至少我是这样解决的。
答案 7 :(得分:1)
(并不是要发表评论)
好吧,它并不真正依赖于那种方式。我可能在这里误解了原来的问题,请原谅我,如果是这样的话。如果你创建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的礼貌,因此值得研究。
答案 8 :(得分:1)
更简单的方法是只使用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上测试
答案 9 :(得分:0)
也许这有助于你。可能这不会转换很多很多关系,但是当你想以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
答案 10 :(得分:0)
这里有很多有趣的解决方案。我的解决方案是使用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()])
答案 11 :(得分:0)
您见过的最佳解决方案。
将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
答案 12 :(得分:0)
@zags的回答很全面,应该足够了,但是#5 方法(这是IMO最好的一种)抛出错误,因此我改进了辅助函数。
由于OP要求将many_to_many
字段转换为主键列表而不是对象列表,因此我增强了功能,因此返回值现在可以JSON序列化-通过将datetime
对象转换为str
和many_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
答案 13 :(得分:0)
将模型转换为字典并保留所有 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__()
值,这会很有用。
在 fields=
中包含关键字参数 exclude=
和 model_to_dict(instance, [...])
使您能够过滤特定字段。
使用详细名称
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}