1. ホーム
  2. python

[解決済み] Django の Model オブジェクトを、全てのフィールドをそのままに dict に変換します。

2022-02-04 03:39:55

質問

で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です。モデルのオブジェクトのセット全体を一つの辞書に変換する

Django のモデルオブジェクトを辞書に変換し、外部キーを保持するにはどうしたらいいですか?

どのように解決するのですか?

インスタンスを辞書に変換する方法は数多くあり、コーナーケースの処理と目的の結果への近さの程度は様々です。


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_many , foreign_key は名前が間違っていて、2つの不要な余分なものが入っています。


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 フレームワーク の 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はdatetimeオブジェクトの代わりに文字列です。


ボーナスラウンド:より良いモデル印刷

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}