Преобразование объекта модели Django в dict со всеми неповрежденными полями

как преобразовать объект модели django в dict с помощью все полей? Все идеально включает в себя внешние ключи и поля с editable=False.

Позвольте уточнить. Предположим, у меня есть модель django, как показано ниже:

from django.db import models

class OtherModel(models.Model): pass

class SomeModel(models.Model):
    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")

в терминале, я сделал следующее:

other_model = OtherModel()
other_model.save()
instance = SomeModel()
instance.value = 1
instance.value2 = 2
instance.reference1 = other_model
instance.save()
instance.reference2.add(other_model)
instance.save()

Я хочу преобразовать это в следующий словарь:

{'created': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
 u'id': 1,
 'reference1': 1,
 'reference2': [1],
 'value': 1,
 'value2': 2}

вопросы с неудовлетворительным ответы:

Django: преобразование всего набора объектов модели в один словарь

как я могу превратить объекты модели Django в словарь и все еще иметь их внешние ключи?

9 ответов


существует множество способов преобразования экземпляра в словарь с различной степенью обработки угловых регистров и близостью к желаемому результату.


1. instance.__dict__

instance.__dict__

возвращает

{'_reference1_cache': <OtherModel: OtherModel object>,
 '_state': <django.db.models.base.ModelState at 0x1f63310>,
 'created': datetime.datetime(2014, 2, 21, 4, 38, 51, 844795, tzinfo=<UTC>),
 'id': 1L,
 'reference1_id': 1L,
 'value': 1,
 'value2': 2}

это самый простой, но отсутствует reference2, reference1 неправильно назван, и в нем есть две нежелательные дополнительные вещи.


2. model_to_dict

from django.forms.models import model_to_dict
model_to_dict(instance)

что возвращает

{u'id': 1L, 'reference1': 1L, 'reference2': [1L], 'value': 1}

это единственный с reference2, но отсутствует неизменяемые поля.


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])

возвращает

{u'id': 1L, 'reference1': 1L, 'value': 1}

это строго хуже, чем стандартный model_to_dict ссылка.


4. query_set.values()

SomeModel.objects.filter(id=instance.id).values()[0]

возвращает

{'created': datetime.datetime(2014, 2, 21, 4, 38, 51, tzinfo=<UTC>),
 u'id': 1L,
 'reference1_id': 1L,
 'value': 1L,
 'value2': 2L}

это тот же выход, что и instance.__dict__ но без дополнительных полей. reference1_id по-прежнему неправильно и reference2 по-прежнему отсутствует.


5. Пользовательская Функция

код имел большую часть ответа. Он явно удалил не редактируемые поля, поэтому удаление этой проверки приводит к следующему коду, который ведет себя по желанию:

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) дает нам точно желаемый результат:

{'created': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
 u'id': 1,
 'reference1': 1,
 'reference2': [1],
 'value': 1,
 'value2': 2}

бонусный раунд: лучшая модель печать

если вы хотите модель django, которая имеет лучший дисплей командной строки python, у ваших моделей дочернего класса следующее:

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):
    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': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
'value': 1, 'value2': 2, 'reference1': 1, u'id': 1, 'reference2': [1]}

Я нашел аккуратное решение, чтобы получить результат:

Предположим, у вас есть модель объекта o:

просто звоните:

type(o).objects.filter(pk=o.pk).values().first()

если вы готовы определить свой собственный метод to_dict, как предложил @karthiker, то это просто сводит эту проблему к проблеме наборов.

>>># 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.

для этого мы можем использовать цикл, который создает новый словарь, в котором есть все элементы, кроме тех, в которых есть подчеркивания. Или, чтобы сэкономить время, мы можем просто добавить их в оригинал дикт поскольку словари-это просто наборы под капотом.

>>>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}

и вы просто возвращен.

С другой стороны, вы не можете использовать подчеркивания в ваших редактируемых=ложных именах полей. С другой стороны, это будет работать для любого набора полей, где пользовательские поля не содержат подчеркивания.

-- EDIT--

это не лучший способ сделать это, но это может сработать как временное решение до более найден прямой метод.

в приведенном ниже примере 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]}
>>>

это должно поставить вас в грубое поле ответа на ваш вопрос, я надеюсь.


самый простой способ,

  1. Если ваш запрос модель.Объекты.get ():

    get () вернет один экземпляр, чтобы вы могли напрямую использовать __dict__ из своего экземпляра

    model_dict = Model.Objects.get().__dict__

  2. на filter () / all ():

    all () / filter () вернет список экземпляров, чтобы вы могли использовать values() получить список объектов.

    model_values = Модель.Объекты.все.)(values ()


@Zags решение было великолепным!

Я бы добавил, однако, условие для полей данных, чтобы сделать его дружественным к JSON.

Бонусный Раунд

если вы хотите модель django, которая имеет лучший дисплей командной строки python, имейте дочерний класс модели следующим образом:

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]}

(не хотел комментировать)

хорошо, это действительно не зависит от типов таким образом. Возможно, я неправильно понял первоначальный вопрос, поэтому простите меня, если это так. Если вы создаете 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)

это в значительной степени то, что у меня есть в vareity мест, и он возвращает хороший JSON через JSONRenderer.

Как я уже сказал, это любезность DjangoRestFramework, поэтому стоит посмотреть на это.


возможно, это поможет вам. Пусть это не скрывает много для многих отношений, но очень удобно, когда вы хотите отправить свою модель в формате 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)

в качестве бонуса это решение в сочетании с пониманием списка по запросу делает хорошее решение, если вы хотите экспортировать свои модели в другую библиотеку. Например, сброс ваших моделей в фрейм данных pandas:

pandas_awesomeness = pd.DataFrame([m.as_dict() for m in SomeModel.objects.all()])

просто 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,
 }