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_key
est 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_dict
invocation 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_id
est toujours faux et many_to_many
est toujours manquant.
5. Fonction personnalisée
Le code pour django model_to_dict
avait 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}
to_dict
et la gérer comme vous le souhaitez.