Django Model 객체를 모든 필드 가있는 dict로 어떻게 변환 합니까? 모두 이상적으로로 외래 키와 필드를 포함 editable=False
합니다.
자세히 설명하겠습니다. 다음과 같은 장고 모델이 있다고 가정 해 봅시다.
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 객체를 사전으로 바꾸고 외래 키를 어떻게 가질 수 있습니까?
답변
다양한 수준의 코너 케이스 처리 및 원하는 결과와의 근접성을 통해 인스턴스를 사전으로 변환하는 방법에는 여러 가지가 있습니다.
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. 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
호출 보다 엄격히 나쁩니다 .
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 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 객체 대신 문자열입니다.
보너스 라운드 : 더 나은 모델 인쇄
더 나은 파이썬 커맨드 라인 디스플레이를 가진 장고 모델을 원한다면, 모델이 다음과 같은 자식 클래스를 갖도록하십시오 :
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}
답변
결과를 얻을 수있는 깔끔한 솔루션을 찾았습니다.
모델 객체가 있다고 가정하십시오 o
.
그냥 전화하십시오 :
type(o).objects.filter(pk=o.pk).values().first()
답변
@ Zags 솔루션은 화려했습니다!
그러나 JSON을 친화적으로 만들기 위해 날짜 필드에 대한 조건을 추가하려고합니다.
보너스 라운드
더 나은 파이썬 명령 줄 표시를 가진 장고 모델을 원한다면 모델에 다음과 같은 자식 클래스를 작성하십시오.
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__
-
에 대한 필터 () / 모든 () :
all () / filter () 는 인스턴스 목록을 반환하므로
values()
객체 목록을 얻는 데 사용할 수 있습니다 .model_values = Model.Objects.all (). values ()
답변
그냥 vars(obj)
객체의 전체 값을 나타냅니다.
>>> obj_attrs = vars(obj)
>>> obj_attrs
{'_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,
}
이것을 추가 할 수도 있습니다
>>> keys = obj_attrs.keys()
>>> temp = [obj_attrs.pop(key) if key.startswith('_') else None for key in keys]
>>> del temp
>>> obj_attrs
{
'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,
}
답변
최신 정보
@zags에 의해 게시 된 새로운 집계 답변은 내 것보다 완벽하고 우아합니다. 대신 해당 답변을 참조하십시오.
실물
@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
{'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는 필터의 값 방법으로 구성됩니다. 모델 자체 에서이 작업을 수행했지만 기계가 다른 모델을 수락하도록 할 수는 없습니다.
>>> 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]}
>>>
그것은 당신의 질문에 대한 대답의 거친 야구장에 당신을 두어야합니다.
답변
흥미로운 솔루션이 많이 있습니다. 내 솔루션은 dict comprehension으로 모델에 as_dict 메소드를 추가하는 것이 었습니다.
def as_dict(self):
return dict((f.name, getattr(self, f.name)) for f in self._meta.fields)
또한이 솔루션을 쿼리에 대한 목록 이해와 함께 사용하면 모델을 다른 라이브러리로 내보내려는 경우 훌륭한 솔루션이됩니다. 예를 들어, 팬더 데이터 프레임에 모델을 덤프하는 경우 :
pandas_awesomeness = pd.DataFrame([m.as_dict() for m in SomeModel.objects.all()])