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


当前回答

这里有很多有趣的解。我的解决方案是在我的模型中添加一个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()])

其他回答

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

无详细名称

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}

我找到了一个很好的解决方法:

假设你有一个模型对象o:

就叫:

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

我已经使用下一个函数转换模型到字典

def model_to_dict(obj):
    return {x: obj.__dict__[x] for x in obj.__dict__ if x in {y.column for y in obj._meta.fields}}

例子

{'id': 8985,
 'title': 'Dmitro',
 'email_address': 'it9+8985@localhost',
 'workspace_id': 'it9',
 'archived': False,
 'deleted': False,
 'inbox': False,
 'read': True,
 'created_at': datetime.datetime(2022, 5, 5, 16, 55, 29, 791844, tzinfo=    <UTC>),
 'creator': 'An So',
 'last_message_id': 500566,
 'stat_data': {'count_messages': 1, 'count_attachments': 0},
 'stat_dirty': False,
 'assign_to_id': None,
 'assigned_at': None,
 'assignment_note': None,
 'initial_last_update_ts': 1651769728,
 'renamed_manually': False,
 'unread_timestamp': datetime.datetime(2022, 5, 5, 16, 55, 29, 842507, tzinfo=<UTC>)}

{'id': 6670,
 'email_id': 473962,
 'message_id': 500620,
 'filename': 'Screenshot.png',
 'size': 6076854,
 'mimetype': 'image/png',
 'aws_key': 'dev/RLpdcza46KFpITDWO_kv_fg2732waccB43z5RmT9/Screenshot.png',
 'aws_key1': '',
 'aws_key_thumb': 'dev/iaCdvcZmUKq-gJim7HT33ID46Ng4WOdxx-TdVuIU/f4b0db49-7f2d-4def-bdc1-8e394f98727f.png',
 's3stored_file_id': 4147}

当我试图使用django-rest框架将django站点转换为API时,我遇到了这个问题。通常django会从数据库中返回三种类型的对象。它们包括一个查询集、一个模型实例和一个分页器对象。对我来说,这些是需要转换的。

查询集

queryset就像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)

Paginator对象

分页器对象是一个包含特定页面的模型对象的对象。

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)

至少我是这么解决的。

我创建了一个小片段,利用django的model_to_dict,但遍历对象的关系。 对于循环依赖项,它终止递归并放入引用依赖项对象的字符串。您可以将其扩展为包含不可编辑字段。

我在测试期间使用它来创建模型快照。

from itertools import chain

from django.db.models.fields.files import FileField, ImageField
from django.forms.models import model_to_dict


def get_instance_dict(instance, already_passed=frozenset()):
    """Creates a nested dict version of a django model instance
    Follows relationships recursively, circular relationships are terminated by putting
    a model identificator `{model_name}:{instance.id}`.
    Ignores image and file fields."""
    instance_dict = model_to_dict(
        instance,
        fields=[
            f
            for f in instance._meta.concrete_fields
            if not isinstance(f, (ImageField, FileField))
        ],
    )

    already_passed = already_passed.union(
        frozenset((f"{instance.__class__.__name__}:{instance.id}",))
    )
    # Go through possible relationships
    for field in chain(instance._meta.related_objects, instance._meta.concrete_fields):
        if (
            (field.one_to_one or field.many_to_one)
            and hasattr(instance, field.name)
            and (relation := getattr(instance, field.name))
        ):
            if (
                model_id := f"{relation.__class__.__name__}:{relation.id}"
            ) in already_passed:
                instance_dict[field.name] = model_id
            else:
                instance_dict[field.name] = get_instance_dict(relation, already_passed)

        if field.one_to_many or field.many_to_many:
            relations = []
            for relation in getattr(instance, field.get_accessor_name()).all():
                if (
                    model_id := f"{relation.__class__.__name__}:{relation.id}"
                ) in already_passed:
                    relations.append(model_id)
                else:
                    relations.append(get_instance_dict(relation, already_passed))
            instance_dict[field.get_accessor_name()] = relations

    return instance_dict