【问题标题】:Convert Django Model object to dict with all of the fields intact将 Django 模型对象转换为 dict 并且所有字段都完好无损
【发布时间】:2014-03-22 10:48:50
【问题描述】:

如何将 django 模型对象转换为具有 所有 字段的字典?理想情况下,所有这些都包括外键和具有可编辑=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 定义以查找与模型关联的字段并在实例上检索它们的值。

标签: python django dictionary django-models django-orm


【解决方案1】:

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


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_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(..., 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 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 模型,让你的模型子类如下:

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 属性)
  • 它还会返回@property 字段吗?
  • 我想知道这些方法将如何处理带注释/聚合的字段?
  • 我要做的是检查 get_FOO_display 并返回该值,而不是实际可能存在的任何值。
【解决方案2】:

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

假设你有一个模型对象o

只要打电话:

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

【讨论】:

  • 这只是我回答中的选项#4
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    @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
    • 当然!但是对代码的这个小改动会增加很多便利!
    【解决方案5】:

    更新

    @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 中删除标记错误的外键。

    为此,我们可以使用一个循环来创建一个新字典,其中包含除下划线之外的所有项目。或者,为了节省时间,我们可以将它们添加到原始 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]}
    >>>
    

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

    【讨论】:

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

    最简单的方法,

    1. 如果您的查询是Model.Objects.get()

      get() 将返回单个实例,因此您可以直接从您的实例中使用 __dict__

      model_dict = Model.Objects.get().__dict__

    2. 对于filter()/all()

      all()/filter() 将返回实例列表,因此您可以使用values() 获取对象列表。

      model_values = Model.Objects.all().values()

    【讨论】:

      【解决方案7】:

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

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

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

      在 Python 3.7 上测试

      【讨论】:

        【解决方案8】:

        当我尝试使用 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)
        

        至少我是这样解决的。

        【讨论】:

          【解决方案9】:

          (没有评论的意思)

          好的,它实际上并不依赖于类型。我可能误解了这里的原始问题,如果是这样,请原谅我。如果您创建 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 提供的,因此值得研究。

          【讨论】:

            【解决方案10】:

            这里有很多有趣的解决方案。我的解决方案是使用 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】:

            也许这对你有帮助。希望这不会隐藏多对多的关系,但是当您想以 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
            

            【讨论】:

              【解决方案12】:

              您见过的最佳解决方案。

              将 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

              【讨论】:

                【解决方案13】:

                @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 部分以反映这一点。
                【解决方案14】:

                将模型转换为字典并保留所有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}
                

                【讨论】:

                  【解决方案15】:

                  我喜欢将模型实例转换为 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)
                  

                  【讨论】:

                    猜你喜欢
                    • 2017-06-28
                    • 2015-04-29
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-07-22
                    • 1970-01-01
                    • 2012-09-05
                    • 1970-01-01
                    • 2013-10-26
                    相关资源
                    最近更新 更多