如何将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对象转换为字典,同时还保留外键?


当前回答

只有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,
   }

其他回答

也许这个能帮到你。也许这不会隐藏多对多的关系,但当你想以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

最简单的方式,

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

更新

@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]}
>>>

我希望这能让你对你的问题有一个大致的答案。

来自@zags的答案是全面的,应该足够了,但#5方法(这是最好的一个IMO)抛出一个错误,所以我改进了helper函数。

由于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

将模型转换为字典并保留所有的外键模型关系。我使用了以下方法:

无详细名称

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}