Convertir l'objet du modèle Django en dict avec tous les champs intacts

258

Comment convertir un objet Django Model en dict avec tous ses champs? Tous comprennent idéalement des clés étrangères et des champs avec editable=False.

Permettez-moi de développer. Disons que j'ai un modèle Django comme celui-ci:

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

Dans le terminal, j'ai fait ce qui suit:

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

Je veux le convertir dans le dictionnaire suivant:

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

Questions avec des réponses insatisfaisantes:

Django: Conversion d'un ensemble complet d'objets d'un modèle en un seul dictionnaire

Comment puis-je transformer des objets du modèle Django en dictionnaire et conserver leurs clés étrangères?

Zags
la source
1
vous pouvez déclarer une méthode appelée to_dictet la gérer comme vous le souhaitez.
karthikr
@karthikr oui, je pourrais. La question est de savoir comment créer une telle méthode. Construire manuellement un dictionnaire à partir de tous les champs du modèle n'est pas une réponse appropriée.
Zags
Je tirerais parti d'une bibliothèque ReST existante comme Django Rest Framework, Tastypie ou Piston car ils fournissent tous des mécanismes pour convertir les instances de modèle django en primitives pour la sérialisation. Si vous êtes plus curieux de savoir comment, vous pouvez parcourir leur code, mais il s'agit principalement de parcourir les _metadéfinitions du modèle pour trouver les champs associés au modèle et récupérer leurs valeurs sur l'instance.
Kevin Stone

Réponses:

526

Il existe de nombreuses façons de convertir une instance en dictionnaire, avec différents degrés de gestion des cas d'angle et de proximité avec le résultat souhaité.


1. instance.__dict__

instance.__dict__

qui revient

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

C'est de loin le plus simple, mais il manque many_to_many, foreign_keyest mal nommé, et il contient deux choses supplémentaires indésirables.


2. model_to_dict

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

qui revient

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

Ceci est le seul avec many_to_many, mais il manque les champs non modifiables.


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

qui revient

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

C'est strictement pire que l' model_to_dictinvocation standard .


4. query_set.values()

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

qui revient

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

C'est la même sortie que instance.__dict__mais sans les champs supplémentaires. foreign_key_idest toujours faux et many_to_manyest toujours manquant.


5. Fonction personnalisée

Le code pour django model_to_dictavait la plupart de la réponse. Il a explicitement supprimé les champs non modifiables, donc la suppression de cette vérification et l'obtention des identifiants des clés étrangères pour plusieurs à plusieurs champs entraîne le code suivant qui se comporte comme vous le souhaitez:

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

Bien que ce soit l'option la plus compliquée, appeler to_dict(instance)nous donne exactement le résultat souhaité:

{'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. Utilisez des sérialiseurs

Le ModelSerialzer de Django Rest Framework vous permet de construire automatiquement un sérialiseur à partir d'un modèle.

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

SomeModelSerializer(instance).data

Retour

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

C'est presque aussi bon que la fonction personnalisée, mais auto_now_add est une chaîne au lieu d'un objet datetime.


Bonus Round: meilleure impression du modèle

Si vous voulez un modèle django qui a un meilleur affichage en ligne de commande python, demandez à vos modèles de classe enfant ce qui suit:

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

Ainsi, par exemple, si nous définissons nos modèles comme tels:

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

L'appel SomeModel.objects.first()donne maintenant une sortie comme celle-ci:

{'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
la source
2
Merci pour cette réponse! Vous pouvez changer le isinstancetest de la solution # 5 (et le bonus) en if f.many_to_many.
dhobbs
1
@dhobbs J'ai modélisé ce code à partir du code de Django model_to_dict, qui utilise isinstance. Je ne sais pas pourquoi ils ont fait ce choix, mais il peut y avoir une bonne raison à cela (comme la many_to_manypropriété introduite dans une version ultérieure)
Zags
cela retournerait-il aussi des @propertychamps?
angrysumit
1
Je me demande comment ces méthodes traiteraient-elles les champs annotés / agrégés?
mehmet
Quelque chose que je fais est de vérifier get_FOO_display et de renvoyer cette valeur au lieu de la valeur qui peut réellement être là.
Bobort
9

J'ai trouvé une solution soignée pour arriver au résultat:

Supposons que vous ayez un objet modèle o:

Il suffit d'appeler:

type(o).objects.filter(pk=o.pk).values().first()
Alfred Huang
la source
10
C'est juste l'option # 4 dans ma réponse
Zags
7

La solution @Zags était magnifique!

J'ajouterais cependant une condition pour les champs de date afin de le rendre compatible JSON.

Bonus Round

Si vous voulez un modèle django qui a un meilleur affichage en ligne de commande python, demandez à vos modèles de classe enfant ce qui suit:

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

Ainsi, par exemple, si nous définissons nos modèles comme tels:

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

L'appel SomeModel.objects.first()donne maintenant une sortie comme celle-ci:

{'created': 1426552454.926738,
'value': 1, 'value2': 2, 'reference1': 1, u'id': 1, 'reference2': [1]}
Diego Freitas Coelho
la source
Si vous souhaitez convertir vers et à partir de JSON, vous devez regarder dans Django Rest Framework ou utiliser quelque chose comme ceci: stackoverflow.com/a/22238613/2800876
Zags
Sûr! Mais cette petite modification de votre code ajoute beaucoup de confort!
Diego Freitas Coelho
4

Manière la plus simple,

  1. Si votre requête est Model.Objects.get ():

    get () renverra une seule instance afin que vous puissiez directement l'utiliser à __dict__partir de votre instance

    model_dict = Model.Objects.get().__dict__

  2. pour filter () / all ():

    all () / filter () renverra la liste des instances afin que vous puissiez l'utiliser values()pour obtenir la liste des objets.

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

Mohideen bin Mohammed
la source
4

juste vars(obj), il indiquera toutes les valeurs de l'objet

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

Vous pouvez également l'ajouter

>>> 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
la source
3

Mettre à jour

La nouvelle réponse agrégée publiée par @zags est plus complète et élégante que la mienne. Veuillez plutôt vous référer à cette réponse.

Original

Si vous êtes prêt à définir votre propre méthode to_dict comme le suggère @karthiker, cela revient simplement à un problème d'ensemble.

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

Nous devons supprimer les clés étrangères mal étiquetées de otherDict .

Pour ce faire, nous pouvons utiliser une boucle qui crée un nouveau dictionnaire qui contient tous les éléments, sauf ceux qui contiennent des traits de soulignement. Ou, pour gagner du temps, nous pouvons simplement les ajouter au dict d' origine car les dictionnaires ne sont que des ensembles sous le capot.

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

Ainsi, nous nous retrouvons avec le dict suivant :

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

Et vous retournez juste ça.

En revanche, vous ne pouvez pas utiliser de traits de soulignement dans vos noms de champ modifiables = faux. À la hausse, cela fonctionnera pour tout ensemble de champs où les champs créés par l'utilisateur ne contiennent pas de traits de soulignement.

Ce n'est pas la meilleure façon de procéder, mais cela pourrait fonctionner comme une solution temporaire jusqu'à ce qu'une méthode plus directe soit trouvée.

Pour l'exemple ci-dessous, dict serait formé sur la base de model_to_dict et otherDict serait formé par la méthode des valeurs du filtre. J'aurais fait cela avec les modèles eux-mêmes, mais je n'arrive pas à ce que ma machine accepte d'autres modèles.

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

Cela devrait vous donner une idée approximative de la réponse à votre question, j'espère.

Gadget
la source
1
Je ne sais pas pour quoi vous essayez d'utiliser reici. S'il s'agit de filtrer les clés contenant des traits de soulignement, ce n'est ni un code correct ni un comportement correct. re.match("_", "reference1_id")les retours Noneet les colonnes légitimes de la base de données peuvent avoir des traits de soulignement dans leurs noms.
Zags
re.match ("_", "reference1_id") ne renvoie aucun, il aurait dû être: re.match (". * _. *", "reference1_id")
Gadget
J'ai apporté quelques modifications pour supprimer le mauvais exemple et en inclure un meilleur. J'ai également changé certaines choses pour exprimer que ce serait une solution temporaire pour un sous-ensemble de tous les modèles. Je n'ai aucune idée de ce que vous feriez pour les modèles avec des soulignements dans leurs editable=falsedomaines. J'essayais simplement de fournir quelque chose avec lequel vous pourriez travailler jusqu'à ce qu'une solution plus canon soit livrée.
Gadget
Peut-être utiliser "_" in stringplutôt que redans ce cas.
Zags
Oui, ce serait une façon beaucoup plus facile de le faire. Il ne m'était pas venu à l'esprit de l'utiliser de cette façon, mais cela a parfaitement du sens maintenant. J'ai changé la réponse à utiliser à la inplace de re.
Gadget
2

Beaucoup de solutions intéressantes ici. Ma solution a été d'ajouter une méthode as_dict à mon modèle avec une compréhension de dict.

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

En prime, cette solution associée à une compréhension de la liste sur une requête est une bonne solution si vous souhaitez exporter vos modèles vers une autre bibliothèque. Par exemple, vider vos modèles dans une trame de données pandas:

pandas_awesomeness = pd.DataFrame([m.as_dict() for m in SomeModel.objects.all()])
t1m0
la source
1
Cela fonctionne très bien pour les champs de valeur comme les chaînes et les entiers, mais aura quelques problèmes avec les clés étrangères et encore plus avec plusieurs à plusieurs champs
Zags
Très bon point! Surtout pour beaucoup à beaucoup. On voudrait mettre des conditions pour gérer ces cas de manière appropriée, ou limiter cette solution à des modèles simples. Merci.
t1m0
1

(ne voulait pas faire le commentaire)

Ok, cela ne dépend pas vraiment des types de cette façon. J'ai peut-être mal compris la question initiale ici, alors pardonnez-moi si c'est le cas. Si vous créez serliazers.py, vous créez des classes contenant des méta-classes.

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

Ensuite, lorsque vous obtenez les données dans la classe d'affichage, vous pouvez:

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

C'est à peu près ce que j'ai dans une variété d'endroits et cela renvoie un joli JSON via le JSONRenderer.

Comme je l'ai dit, cela est une gracieuseté de DjangoRestFramework, donc cela vaut la peine d'y réfléchir.

nathj07
la source
1

La façon la plus simple est de simplement utiliser pprint, qui est en Python de base

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

Cela donne une sortie qui ressemble à json.dumps(..., indent = 4)mais gère correctement les types de données étranges qui pourraient être incorporés dans votre instance de modèle, tels que ModelStateet UUID, etc.

Testé sur Python 3.7

user5359531
la source
0

Peut-être que cela vous aidera. Puisse cela ne pas cacher beaucoup de relations, mais c'est assez pratique lorsque vous souhaitez envoyer votre modèle au format 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
la source
0

La meilleure solution que vous ayez jamais vue.

Convertissez l'instance django.db.models.Model et tous les champs de fonction ForeignKey, ManyToManyField et @Property associés 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
la source
0

La réponse de @zags est complète et devrait suffire, mais la méthode # 5 (qui est la meilleure IMO) génère une erreur, j'ai donc amélioré la fonction d'assistance.

Comme l'OP a demandé la conversion des many_to_manychamps en une liste de clés primaires plutôt qu'en une liste d'objets, j'ai amélioré la fonction afin que la valeur de retour soit désormais sérialisable JSON - en convertissant les datetimeobjets en stret les many_to_manyobjets en une liste d'identifiants.

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
la source
Quelle est l'erreur que vous obtenez? Je suis heureux de mettre à jour la réponse
Zags
Actuellement, la fonctionnalité des boucles est identique concrete_fieldset m2m_fieldssemble identique, donc en supposant que la m2m_fieldsboucle a une implémentation incorrecte ici.
Daniel Himmelstein
@Zags, c'est l'erreur AttributeError: 'list' object has no attribute 'values_list' dont je n'ai pas trouvé la raison.
J'utilise
@ daniel-himmelstein merci de l'avoir signalé, j'ai corrigé le code. la raison des boucles identiques était due à l'exécution de différentes opérations dans mon code local et j'ai oublié de l'optimiser pour la réponse SO.
Armin Hemati Nik
@ArminHemati Django a changé l'implémentation de field.value_from_objectet, par conséquent, de model_to_dict. J'ai mis à jour la section 5 de ma réponse pour refléter cela.
Zags