Django モデルオブジェクトを、すべてのフィールドをそのままにして辞書に変換する 質問する

Django モデルオブジェクトを、すべてのフィールドをそのままにして辞書に変換する 質問する

Django モデル オブジェクトを、すべてのフィールドを含む辞書に変換するにはどうすればよいでしょうか。理想的には、すべてに外部キーと 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

インスタンスを辞書に変換する方法は多数あり、コーナーケースの処理の度合いや目的の結果への近さも異なります。


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 つ含まれています。


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 フレームワークの ModelSerializer を使用すると、モデルからシリアライザーを自動的に構築できます。

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}

おすすめ記事