Convierta el objeto del modelo Django en dict con todos los campos intactos

258

¿Cómo se convierte un objeto Modelo Django en un dict con todos sus campos? Todo incluye idealmente claves externas y campos con editable=False.

Déjame elaborar. Digamos que tengo un modelo de Django como el siguiente:

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

En la terminal, he hecho lo siguiente:

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

Quiero convertir esto al siguiente diccionario:

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

Preguntas con respuestas insatisfactorias:

Django: Convertir un conjunto completo de objetos de un Modelo en un solo diccionario

¿Cómo puedo convertir los objetos del Modelo Django en un diccionario y aún tener sus claves foráneas?

Zags
fuente
1
puede declarar un método llamado to_dicty manejarlo de la manera que desee.
karthikr
@karthikr sí, podría. La pregunta es cómo crear tal método. La construcción manual de un diccionario a partir de todos los campos del modelo no es una respuesta adecuada.
Zags
Aprovecharía una biblioteca ReST existente como Django Rest Framework, Tastypie o Piston, ya que todas proporcionan mecanismos para convertir las instancias del modelo django en primitivas para la serialización. Si tiene más curiosidad sobre cómo, puede mirar a través de su código, pero se trata principalmente de recorrer las _metadefiniciones del modelo para encontrar los campos asociados con el modelo y recuperar sus valores en la instancia.
Kevin Stone

Respuestas:

526

Hay muchas formas de convertir una instancia en un diccionario, con diversos grados de manejo de mayúsculas y de proximidad al resultado deseado.


1) instance.__dict__

instance.__dict__

que vuelve

{'_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}

Este es, con mucho, el más simple, pero falta many_to_many, foreign_keytiene un nombre incorrecto y tiene dos cosas adicionales no deseadas.


2) model_to_dict

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

que vuelve

{'foreign_key': 2,
 'id': 1,
 'many_to_many': [<OtherModel: OtherModel object>],
 'normal_value': 1}

Este es el único con many_to_many, pero le faltan los campos no editables.


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

que vuelve

{'foreign_key': 2, 'id': 1, 'normal_value': 1}

Esto es estrictamente peor que la model_to_dictinvocación estándar .


4) query_set.values()

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

que vuelve

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

Este es el mismo resultado instance.__dict__pero sin los campos adicionales. foreign_key_idtodavía está mal y many_to_manytodavía falta.


5. Función personalizada

El código para django model_to_dicttenía la mayor parte de la respuesta. Eliminó explícitamente los campos no editables, por lo que al eliminar esa verificación y obtener los identificadores de claves foráneas para muchos o muchos campos, se obtiene el siguiente código que se comporta como se desea:

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

Si bien esta es la opción más complicada, las llamadas to_dict(instance)nos dan exactamente el resultado deseado:

{'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. Use serializadores

ModelSerialzer de Django Rest Framework le permite construir un serializador automáticamente a partir de un modelo.

from rest_framework import serializers
class SomeModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = SomeModel
        fields = "__all__"

SomeModelSerializer(instance).data

devoluciones

{'auto_now_add': '2018-12-20T21:34:29.494827Z',
 'foreign_key': 2,
 'id': 1,
 'many_to_many': [2],
 'normal_value': 1,
 'readonly_value': 2}

Esto es casi tan bueno como la función personalizada, pero auto_now_add es una cadena en lugar de un objeto de fecha y hora.


Bonus Round: mejor impresión de modelos

Si desea un modelo django que tenga una mejor visualización de la línea de comandos de Python, haga que sus modelos sean de clase secundaria:

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

Entonces, por ejemplo, si definimos nuestros modelos como tales:

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

Llamar SomeModel.objects.first()ahora da una salida como esta:

{'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}
Zags
fuente
2
Gracias por esta respuesta! Puede cambiar la isinstanceprueba en la solución n. ° 5 (y la bonificación) a if f.many_to_many.
dhobbs
1
@dhobbs Modelé ese código del código de Django model_to_dict, que usa isinstance. No estoy seguro de por qué hicieron esta elección, pero puede haber una buena razón para ello (como la many_to_manypropiedad que se introdujo en una versión posterior)
Zags
¿también devolvería @propertycampos?
angrysumit
1
Me pregunto cómo tratarían estos métodos los campos anotados / agregados.
mehmet
Algo que hago es buscar get_FOO_display y devolver ese valor en lugar de cualquier valor que pueda estar allí.
Bobort
9

Encontré una solución ordenada para llegar al resultado:

Supongamos que tiene un objeto modelo o:

Solo llama:

type(o).objects.filter(pk=o.pk).values().first()
Alfred Huang
fuente
10
Esta es solo la opción # 4 en mi respuesta
Zags
7

¡La solución de @Zags fue hermosa!

Sin embargo, agregaría una condición para los campos de fecha para que sea compatible con JSON.

Ronda de bonificación

Si desea un modelo django que tenga una mejor visualización de la línea de comandos de Python, haga que sus modelos sean de la siguiente clase:

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

Entonces, por ejemplo, si definimos nuestros modelos como tales:

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

Llamar SomeModel.objects.first()ahora da una salida como esta:

{'created': 1426552454.926738,
'value': 1, 'value2': 2, 'reference1': 1, u'id': 1, 'reference2': [1]}
Diego Freitas Coelho
fuente
Si desea convertir ay desde JSON, debe buscar en Django Rest Framework o usar algo como esto: stackoverflow.com/a/22238613/2800876
Zags
¡Por supuesto! ¡Pero este pequeño cambio en su código agrega una gran conveniencia!
Diego Freitas Coelho
4

La forma más simple

  1. Si su consulta es Model.Objects.get ():

    get () devolverá una sola instancia para que pueda usarla directamente __dict__ desde su instancia

    model_dict = Model.Objects.get().__dict__

  2. para filter () / all ():

    all () / filter () devolverá una lista de instancias para que pueda usarvalues() para obtener una lista de objetos.

    valores_modelo = Model.Objects.all (). values ​​()

Mohideen bin Mohammed
fuente
4

solo vars(obj), indicará los valores completos del objeto

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

Puedes agregar esto también

>>> 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,
   }
A.Raouf
fuente
3

Actualizar

La nueva respuesta agregada publicada por @zags es más completa y elegante que la mía. Por favor, consulte esa respuesta en su lugar.

Original

Si está dispuesto a definir su propio método to_dict como sugirió @karthiker, entonces eso simplemente reduce este problema a un problema de conjuntos.

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

Necesitamos eliminar las claves externas mal etiquetadas de otherDict .

Para hacer esto, podemos usar un bucle que crea un nuevo diccionario que tiene todos los elementos, excepto aquellos con guiones bajos. O, para ahorrar tiempo, simplemente podemos agregarlos al dict original ya que los diccionarios son solo conjuntos bajo el capó.

>>>for item in otherDict.items():
...    if "_" not in item[0]:
...            dict.update({item[0]:item[1]})
...
>>>

Así nos quedamos con el siguiente dict :

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

Y acabas de devolver eso.

En el lado negativo, no puede usar guiones bajos en sus nombres de campo editables = falsos. Por el lado positivo, esto funcionará para cualquier conjunto de campos donde los campos creados por el usuario no contengan guiones bajos.

Esta no es la mejor manera de hacerlo, pero podría funcionar como una solución temporal hasta que se encuentre un método más directo.

Para el siguiente ejemplo, dict se formaría en base a model_to_dict y otherDict se formaría mediante el método de valores de filtro. Habría hecho esto con los modelos mismos, pero no puedo hacer que mi máquina acepte otro modelo.

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

Espero que eso te ponga en un estadio aproximado de la respuesta a tu pregunta.

Artilugio
fuente
1
No estoy seguro de qué estás tratando de usar reaquí. Si se trata de filtrar claves con guiones bajos, no se trata de un código correcto ni de un comportamiento correcto. re.match("_", "reference1_id")devoluciones Noney columnas legítimas en la base de datos pueden tener guiones bajos en sus nombres.
Zags
re.match ("_", "reference1_id") no devuelve None, debería haber sido: re.match (". * _. *", "reference1_id")
Gadget
Hice algunos cambios para eliminar el mal ejemplo e incluir uno mejor. También cambié algunas cosas para expresar que esta sería una solución temporal para un subconjunto de todos los modelos. No tengo idea de lo que haría para modelos con guiones bajos en sus editable=falsecampos. Solo estaba tratando de proporcionar algo con lo que podría trabajar hasta que se pudiera entregar una solución más canónica.
Gadget
Tal vez usar en "_" in stringlugar de reen ese caso.
Zags
Sí, esa sería una forma mucho más fácil de hacerlo. No se me había ocurrido usarlo de esta manera, pero ahora tiene mucho sentido. He cambiado la respuesta para usar en inlugar de re.
Gadget
2

Muchas soluciones interesantes aquí. Mi solución fue agregar un método as_dict a mi modelo con una comprensión dict.

def as_dict(self):
    return dict((f.name, getattr(self, f.name)) for f in self._meta.fields)

Como beneficio adicional, esta solución combinada con una comprensión de la lista sobre una consulta lo convierte en una buena solución si desea exportar sus modelos a otra biblioteca. Por ejemplo, volcar sus modelos en un marco de datos de pandas:

pandas_awesomeness = pd.DataFrame([m.as_dict() for m in SomeModel.objects.all()])
t1m0
fuente
1
Esto funciona bien para campos de valor como cadenas e ints, pero tendrá algunos problemas con las claves foráneas y aún más con muchos campos
Zags
Muy buen punto! Especialmente para muchos a muchos. Uno querría poner algunos condicionales para manejar esos casos adecuadamente, o limitar esta solución a modelos simples. Gracias.
t1m0
1

(no quise hacer el comentario)

Ok, realmente no depende de los tipos de esa manera. Puede que haya entendido mal la pregunta original aquí, así que perdóname si ese es el caso. Si creas serliazers.py, entonces creas clases que tienen metaclases.

Class MyModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = modelName
        fields =('csv','of','fields')

Luego, cuando obtenga los datos en la clase de vista, puede:

model_data - Model.objects.filter(...)
serializer = MyModelSerializer(model_data, many=True)
return Response({'data': serilaizer.data}, status=status.HTTP_200_OK)

Eso es más o menos lo que tengo en una gran variedad de lugares y devuelve un buen JSON a través del JSONRenderer.

Como dije, esto es cortesía de DjangoRestFramework, por lo que vale la pena investigarlo.

nathj07
fuente
1

La forma más fácil es simplemente usar pprint, que está en Python base

import pprint
item = MyDjangoModel.objects.get(name = 'foo')
pprint.pprint(item.__dict__, indent = 4)

Esto proporciona un resultado similar json.dumps(..., indent = 4)pero que maneja correctamente los tipos de datos extraños que podrían estar incrustados en la instancia de su modelo, como ModelStateyUUID , etc.

Probado en Python 3.7

usuario5359531
fuente
0

Quizás esto te ayude. Que esto no encubra una relación de muchos a muchos, pero es bastante útil cuando desea enviar su modelo en formato 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
Pjl
fuente
0

La mejor solución que hayas visto.

Convierta la instancia django.db.models.Model y todos los campos de funciones ForeignKey, ManyToManyField y @Property relacionados en 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: /programming/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

Tony
fuente
0

La respuesta de @zags es exhaustiva y debería ser suficiente, pero el método # 5 (que es el mejor IMO) arroja un error, así que mejoré la función auxiliar.

Como el OP solicitó convertir los many_to_manycampos en una lista de claves primarias en lugar de una lista de objetos, mejoré la función para que el valor de retorno ahora sea serializable JSON, al convertir datetimeobjetos en stry many_to_manyobjetos en una lista de 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
Armin Hemati Nik
fuente
¿Cuál es el error que obtienes? Estoy feliz de actualizar la respuesta
Zags 01 de
Actualmente la funcionalidad de los bucles a través concrete_fieldsy m2m_fieldsparecer idénticos, por lo que suponiendo que el m2m_fieldsbucle tiene una aplicación incorrecta aquí.
Daniel Himmelstein
@Zags el error es AttributeError: 'list' object has no attribute 'values_list' que no pude encontrar la razón detrás de esto. Estoy usando Django 2.1.1
Armin Hemati Nik
@ daniel-himmelstein gracias por señalar, arreglé el código. La razón de los bucles idénticos se debió a realizar diferentes operaciones en mi código local y olvidé optimizarlo para la respuesta SO.
Armin Hemati Nik
@ArminHemati Django cambió la implementación de field.value_from_objecty, como resultado, de model_to_dict. He actualizado la sección 5 de mi respuesta para reflejar esto.
Zags