首页 文章

将Django Model对象转换为dict,所有字段都保持不变

提问于
浏览
148

如何将django Model对象转换为带有 all 字段的dict?理想情况下,所有内容都包含外键和带有editable = False的字段 .

让我详细说明一下 . 假设我有一个类似以下的django模型:

from django.db import models

class OtherModel(models.Model): pass

class SomeModel(models.Model):
    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")

在终端中,我做了以下事情:

other_model = OtherModel()
other_model.save()
instance = SomeModel()
instance.value = 1
instance.value2 = 2
instance.reference1 = other_model
instance.save()
instance.reference2.add(other_model)
instance.save()

我想将其转换为以下字典:

{'created': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
 u'id': 1,
 'reference1': 1,
 'reference2': [1],
 'value': 1,
 'value2': 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?

9 回答

  • 1

    有许多方法可以将实例转换为字典,具有不同程度的角落处理和与所需结果的接近程度 .


    1.实例.__ dict__

    instance.__dict__
    

    返回

    {'_reference1_cache': <OtherModel: OtherModel object>,
     '_state': <django.db.models.base.ModelState at 0x1f63310>,
     'created': datetime.datetime(2014, 2, 21, 4, 38, 51, 844795, tzinfo=<UTC>),
     'id': 1L,
     'reference1_id': 1L,
     'value': 1,
     'value2': 2}
    

    这是迄今为止最简单的,但缺少 reference2reference1 是错误名称,并且它有两个不需要的额外的东西 .


    2. model_to_dict

    from django.forms.models import model_to_dict
    model_to_dict(instance)
    

    返回

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

    这是唯一一个 reference2 ,但缺少不可编辑的字段 .


    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])
    

    返回

    {u'id': 1L, 'reference1': 1L, 'value': 1}
    

    这严格比标准 model_to_dict 调用更糟糕 .


    4. query_set.values()

    SomeModel.objects.filter(id=instance.id).values()[0]
    

    返回

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

    这与 instance.__dict__ 的输出相同,但没有额外的字段 . reference1_id 仍然错误, reference2 仍然缺失 .


    5.自定义功能

    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) 会给我们提供所需的结果:

    {'created': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
     u'id': 1,
     'reference1': 1,
     'reference2': [1],
     'value': 1,
     'value2': 2}
    

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

    如果你想要一个具有更好的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))
                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': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
    'value': 1, 'value2': 2, 'reference1': 1, u'id': 1, 'reference2': [1]}
    
  • 1

    @Zags解决方案很华丽!

    不过,我想补充一下datefields的条件,以使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

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

    这里有很多有趣的解决方案 . 我的解决方案是使用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()])
    
  • 268

    如果您愿意定义自己的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中删除标记错误的外键 .

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

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

    我希望,那应该让你对你的问题的答案进行粗略的讨论 .

  • 0

    最简单的方式,

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

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

    model_dict = Model.Objects.get().__dict__

    • for filter()/all():

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

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

  • 3

    (并不是要发表评论)

    好吧,它并不真正依赖于那种类型 . 我可能在这里误解了原来的问题,请原谅我,如果是这样的话 . 如果您创建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的礼貌,因此值得研究 .

  • 4

    也许这有助于你 . 可能这不会隐瞒很多很多关系,但是非常方便你想以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
    
  • 0

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

    假设您有一个模型对象 o

    只需致电:

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

相关问题