どのようにしてDjango Modelオブジェクトをallそのフィールドを持つ辞書に変換するのですか?すべて理想的には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}
満足できない答えがある質問:
インスタンスを辞書に変換する方法はたくさんありますが、コーナーケースの処理の程度や目的の結果への近さはさまざまです。
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つの望ましくない余分なことがあります。
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
を持つ唯一のものですが、編集不可能なフィールドがありません。
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
呼び出しよりも厳密に悪いです。
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
はまだありません。
Djangoのmodel_to_dict
のコードがほとんどの答えでした。編集不可能なフィールドを明示的に削除したため、このチェックを削除すると、次のコードが必要に応じて動作します。
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
これは最も複雑なオプションですが、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}
Django Rest Framework の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 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
したがって、たとえば、モデルを次のように定義したとします。
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}
私は結果を得るためにきちんとした解決策を見つけました:
モデルオブジェクトo
があるとします。
ただ電話してください:
type(o).objects.filter(pk=o.pk).values().first()
@Zagsのソリューションはゴージャスでした!
ただし、JSONに優しくするために、日付フィールドの条件を追加します。
より良いPythonコマンドライン表示を持つDjangoモデルが欲しいなら、あなたのモデルの子クラスに以下を持たせてください:
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
したがって、たとえば、モデルを次のように定義したとします。
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")
SomeModel.objects.first()
を呼び出すと、このような出力が得られます。
{'created': 1426552454.926738,
'value': 1, 'value2': 2, 'reference1': 1, u'id': 1, 'reference2': [1]}
最も簡単な方法
クエリがModel.Objects.get():の場合
get()は単一のインスタンスを返すので、あなたのインスタンスから直接__dict__
を使うことができます。
model_dict = Model.Objects.get().__dict__
filter()/ all():の場合
all()/ filter()はインスタンスのリストを返すので、values()
を使ってオブジェクトのリストを取得できます。
model_values = Model.Objects.all()。values()
ちょうどvars(obj)
、それはオブジェクトの全体の値を述べます
{'_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,
}
あなたが@karthikerが提案したようなあなた自身のto_dictメソッドを定義しても構わないと思っているならば、それはちょうどセット問題にこの問題を解決します。
>>># 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に追加することができます。辞書は単にボンネットの下に置かれているからです。
>>>for item in otherDict.items():
... if "_" not in item[0]:
... dict.update({item[0]:item[1]})
...
>>>
したがって、次のようになりますdict:
>>>dict
{'created': datetime.datetime(2014, 2, 21, 4, 38, 51, tzinfo=<UTC>),
u'id': 1L,
'reference1': 1L,
'reference2': [1L],
'value': 1,
'value2': 2L}
そしてあなたはそれを返します。
欠点は、editable = falseのフィールド名にアンダースコアを使用できないことです。逆に言えば、これは、ユーザー作成フィールドにアンダースコアが含まれていないフィールドのセットに対して有効です。
- 編集 -
これはこれを行う最良の方法ではありませんが、より直接的な方法が見つかるまで一時的な解決策として機能する可能性があります。
以下の例では、dictはmodel_to_dictに基づいて形成され、otherDictはfilterのvaluesメソッドによって形成されます。私はモデル自体でこれをしていたでしょう、しかし私は私のマシンにotherModelを受け付けることができません。
>>> 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]}
>>>
それであなたはあなたの質問に対する答えの大まかな評価を得られるはずです、私は願っています。
(コメントするつもりはなかった)
さて、それは本当にそのように型に依存しません。私はここで元の質問を誤って理解したかもしれないので、そうであるならば私を許してください。 serliazers.pyを作成したら、そこにメタクラスを持つクラスを作成します。
Class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = modelName
fields =('csv','of','fields')
ビュークラスのデータを取得すると、次のことができます。
model_data - Model.objects.filter(...)
serializer = MyModelSerializer(model_data, many=True)
return Response({'data': serilaizer.data}, status=status.HTTP_200_OK)
それは私がさまざまな場所で持っているもののほとんどであり、JSONRendererを介してNice JSONを返します。
私が言ったように、これはDjangoRestFrameworkのおかげですので、それを検討する価値があります。
あなたが今まで見た中で最高の解決策。
Django.db.models.Modelインスタンスとそれに関連するすべてのForeignKey、ManyToManyFieldおよび@Property関数フィールドをdictに変換します。
"""
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.core.exceptions
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.Iterable]=None, exclude: typing.Optional[typing.Iterable]=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:
self_fields.add(splits[0])
field_name = splits[0]
child_fields.setdefault(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 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:
data[f.name] = None
try:
foreign_inst = getattr(self, f.name)
except Django.core.exceptions.ObjectDoesNotExist:
pass
else:
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)
)
Elif foreign_inst is not None:
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, PrintableModel) and hasattr(value, "to_dict"):
data[name] = value.to_dict(
fields=child_fields.get(name),
exclude=child_exclude.get(name)
)
Elif hasattr(value, "_meta"):
# make sure it is a instance of Django.db.models.fields.Field
data[name] = Django.forms.models.model_to_dict(
value,
fields=child_fields.get(name),
exclude=child_exclude.get(name),
)
Elif isinstance(value, (set, )):
data[name] = list(value)
else:
data[name] = value
return data
https://Gist.github.com/shuge/f543dc2094a3183f69488df2bfb51a52
多分これはあなたを助ける。これは多対多の関係を隠していないかもしれませんが、あなたのモデルをJSONフォーマットで送信したい場合にはかなり便利です。
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
ここにたくさんの面白い解決策があります。私の解決策は、辞書理解を使ってモデルにas_dictメソッドを追加することでした。
def as_dict(self):
return dict((f.name, getattr(self, f.name)) for f in self._meta.fields)
おまけとして、このソリューションとクエリのリスト内包表記を組み合わせることで、モデルを別のライブラリにエクスポートする場合に、Niceソリューションを利用できます。たとえば、モデルをパンダデータフレームにダンプします。
pandas_awesomeness = pd.DataFrame([m.as_dict() for m in SomeModel.objects.all()])
@zagsからの回答は包括的であり、十分なはずです。 しかし、#5メソッド(これがIMOで最も優れている)はエラーをスローするため、ヘルパー関数を改善しました。
OPがmany_to_many
フィールドをオブジェクトのリストではなく主キーのリストに変換するよう要求したため、関数を拡張して戻り値がJSONシリアル化できるようにしました-datetime
オブジェクトをstr
およびmany_to_many
オブジェクトをIDのリストに追加します。
import datetime
def ModelToDict(instance):
'''
Returns a dictionary object containing complete field-value pairs of the given instance
Convertion rules:
datetime.date --> str
many_to_many --> list of id's
'''
concrete_fields = instance._meta.concrete_fields
m2m_fields = instance._meta.many_to_many
data = {}
for field in concrete_fields:
key = field.name
value = field.value_from_object(instance)
if type(value) == datetime.datetime:
value = str(field.value_from_object(instance))
data[key] = value
for field in m2m_fields:
key = field.name
value = field.value_from_object(instance)
data[key] = [rel.id for rel in value]
return data