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
to_dict
et la gérer comme vous le souhaitez._meta
définitions du modèle pour trouver les champs associés au modèle et récupérer leurs valeurs sur l'instance.Réponses:
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__
qui revient
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
qui revient
Ceci est le seul avec
many_to_many
, mais il manque les champs non modifiables.3.
model_to_dict(..., fields=...)
qui revient
C'est strictement pire que l'
model_to_dict
invocation standard .4.
query_set.values()
qui revient
C'est la même sortie que
instance.__dict__
mais sans les champs supplémentaires.foreign_key_id
est toujours faux etmany_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:Bien que ce soit l'option la plus compliquée, appeler
to_dict(instance)
nous donne exactement le résultat souhaité:6. Utilisez des sérialiseurs
Le ModelSerialzer de Django Rest Framework vous permet de construire automatiquement un sérialiseur à partir d'un modèle.
Retour
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:
Ainsi, par exemple, si nous définissons nos modèles comme tels:
L'appel
SomeModel.objects.first()
donne maintenant une sortie comme celle-ci:la source
isinstance
test de la solution # 5 (et le bonus) enif f.many_to_many
.model_to_dict
, qui utiliseisinstance
. Je ne sais pas pourquoi ils ont fait ce choix, mais il peut y avoir une bonne raison à cela (comme lamany_to_many
propriété introduite dans une version ultérieure)@property
champs?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:
la source
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:
Ainsi, par exemple, si nous définissons nos modèles comme tels:
L'appel
SomeModel.objects.first()
donne maintenant une sortie comme celle-ci:la source
Manière la plus simple,
Si votre requête est Model.Objects.get ():
get () renverra une seule instance afin que vous puissiez directement l'utiliser à
__dict__
partir de votre instancemodel_dict =
Model.Objects.get().__dict__
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 ()
la source
juste
vars(obj)
, il indiquera toutes les valeurs de l'objetVous pouvez également l'ajouter
la source
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.
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.
Ainsi, nous nous retrouvons avec le dict suivant :
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.
Cela devrait vous donner une idée approximative de la réponse à votre question, j'espère.
la source
re
ici. 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 retoursNone
et les colonnes légitimes de la base de données peuvent avoir des traits de soulignement dans leurs noms.editable=false
domaines. J'essayais simplement de fournir quelque chose avec lequel vous pourriez travailler jusqu'à ce qu'une solution plus canon soit livrée."_" in string
plutôt quere
dans ce cas.in
place dere
.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.
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:
la source
(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.
Ensuite, lorsque vous obtenez les données dans la classe d'affichage, vous pouvez:
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.
la source
La façon la plus simple est de simplement utiliser
pprint
, qui est en Python de baseCela 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 queModelState
etUUID
, etc.Testé sur Python 3.7
la source
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.
la source
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.
https://gist.github.com/shuge/f543dc2094a3183f69488df2bfb51a52
la source
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_many
champs 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 lesdatetime
objets enstr
et lesmany_to_many
objets en une liste d'identifiants.la source
concrete_fields
etm2m_fields
semble identique, donc en supposant que lam2m_fields
boucle a une implémentation incorrecte ici.AttributeError: 'list' object has no attribute 'values_list'
dont je n'ai pas trouvé la raison.field.value_from_object
et, par conséquent, demodel_to_dict
. J'ai mis à jour la section 5 de ma réponse pour refléter cela.