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}
満足のいく回答が得られなかった質問:
ベストアンサー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_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 フレームワークの 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}