Convert Django Model object to dict with all of the fields intact
如何将django模型对象转换为包含所有字段的dict?理想情况下,所有字段都包含外键和editable=false的字段。
让我详细说明一下。假设我有一个像下面这样的django模型:
1 2 3 4 5 6 7 8 9 10 | 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") |
在终端中,我完成了以下操作:
1 2 3 4 5 6 7 8 9 | 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() |
我要将此转换为以下词典:
1 2 3 4 5 6 | {'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:将模型对象的整个集合转换为单个字典
如何将django模型对象转换为字典,并且仍然具有它们的外键?
有许多方法可以将实例转换为字典,不同程度地处理角大小写,并接近所需的结果。
1。1 | instance.__dict__ |
哪些回报
1 2 3 4 5 6 7 | {'_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} |
这是迄今为止最简单的,但缺少了
1 2 | from django.forms.models import model_to_dict model_to_dict(instance) |
哪些回报
1 2 3 4 | {'foreign_key': 2, 'id': 1, 'many_to_many': [<OtherModel: OtherModel object>], 'normal_value': 1} |
这是唯一一个使用
1 2 | from django.forms.models import model_to_dict model_to_dict(instance, fields=[field.name for field in instance._meta.fields]) |
哪些回报
1 | {'foreign_key': 2, 'id': 1, 'normal_value': 1} |
这比标准的
1 | SomeModel.objects.filter(id=instance.id).values()[0] |
哪些回报
1 2 3 4 5 | {'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} |
这与
Django的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 |
虽然这是最复杂的选择,但调用
1 2 3 4 5 6 | {'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。使用串行化器
DjangoRest框架的ModelSerialzer允许您从模型自动构建序列化程序。
1 2 3 4 5 6 7 | from rest_framework import serializers class SomeModelSerializer(serializers.ModelSerializer): class Meta: model = SomeModel fields ="__all__" SomeModelSerializer(instance).data |
收益率
1 2 3 4 5 6 | {'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模型,请让您的models子类如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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 |
例如,如果我们这样定义我们的模型:
1 2 3 4 5 6 7 8 | 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") |
调用
1 2 3 4 5 6 | {'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 | type(o).objects.filter(pk=o.pk).values().first() |
@扎格斯的解决方案太棒了!
不过,我会为datefields添加一个条件,以使它更适合JSON。
奖金轮如果您想要一个具有更好的python命令行显示的django模型,请让您的models子类如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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 |
例如,如果我们这样定义我们的模型:
1 2 3 4 5 6 7 8 | 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") |
调用
1 2 | {'created': 1426552454.926738, 'value': 1, 'value2': 2, 'reference1': 1, u'id': 1, 'reference2': [1]} |
最简单的方法,
如果查询是model.objects.get():
get()将返回单个实例,这样您就可以直接从实例中使用
型号:dict=
对于filter()/all():
all()/filter()将返回实例列表,因此可以使用
model_values=model.objects.all().values())
如果您愿意像@karthiker建议的那样定义自己的to-dict方法,那么这将把问题归结为集合问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | >>># 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中,因为字典只是设置在风帽下。
1 2 3 4 5 | >>>for item in otherDict.items(): ... if"_" not in item[0]: ... dict.update({item[0]:item[1]}) ... >>> |
因此,留给我们的是以下口述:
1 2 3 4 5 6 7 | >>>dict {'created': datetime.datetime(2014, 2, 21, 4, 38, 51, tzinfo=<UTC>), u'id': 1L, 'reference1': 1L, 'reference2': [1L], 'value': 1, 'value2': 2L} |
你只要把它还给我。
不利的是,在可编辑=假字段名中不能使用下划线。另一方面,这将适用于任何一组字段,其中用户创建的字段不包含下划线。
--编辑——
这不是最好的方法,但在找到更直接的方法之前,它可以作为临时解决方案。
对于下面的示例,dict将基于model_to_dict形成,otherdict将通过filter的values方法形成。我本来可以用模型自己来做的,但我不能让我的机器接受其他模型。
1 2 3 4 5 6 7 8 9 10 | >>> 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]} >>> |
我希望这会让你对你的问题有一个大致的答案。
仅限于
1 2 3 4 5 6 7 8 | {'_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, } |
最好的解决方案。
将django.db.models.model实例和所有相关的foreignkey、manytomanyfield和@property函数字段转换为dict。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | """ 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.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.Set]=None, exclude: typing.Optional[typing.Set]=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: field_name = splits[0] if field_name not in child_fields: child_fields[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 not getattr(f, 'editable', False): continue 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: foreign_inst = getattr(self, f.name) 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)) else: 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, (set, )): data[name] = list(value) else: data[name] = value return data |
https://gist.github.com/shuge/f543dc2094a183f69488df2bfb51a52
(不是有意发表评论)
好吧,这并不取决于类型。我可能误解了最初的问题,如果是这样的话,请原谅我。如果创建serliazers.py,那么在其中创建具有元类的类。
1 2 3 4 | Class MyModelSerializer(serializers.ModelSerializer): class Meta: model = modelName fields =('csv','of','fields') |
然后,当您在视图类中获取数据时,可以:
1 2 3 | model_data - Model.objects.filter(...) serializer = MyModelSerializer(model_data, many=True) return Response({'data': serilaizer.data}, status=status.HTTP_200_OK) |
这几乎就是我在不同地方所拥有的,它通过JSONrenderer返回了很好的JSON。
正如我所说,这是由Djangorestframework提供的,所以值得研究一下。
这里有很多有趣的解决方案。我的解决方案是在我的模型中添加一个"as-dict"方法,并进行听写理解。
1 2 | def as_dict(self): return dict((f.name, getattr(self, f.name)) for f in self._meta.fields) |
另外,如果您希望将模型导出到另一个库中,此解决方案与对查询的列表理解相结合可以提供一个很好的解决方案。例如,将您的模型转储到熊猫数据帧中:
1 | pandas_awesomeness = pd.DataFrame([m.as_dict() for m in SomeModel.objects.all()]) |
也许这对你有帮助。也许这并不能隐藏多对多的关系,但是当您希望以JSON格式发送模型时,这非常方便。
1 2 3 4 5 6 7 8 9 10 11 12 | 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 |