Es gibt viele Möglichkeiten, eine Instanz in ein Wörterbuch zu konvertieren, mit unterschiedlichen Grad an Randfallbehandlung und Nähe zum gewünschten Ergebnis.
1. instance.__dict__
instance.__dict__
was zurückgibt
{'_foreign_key_cache': ,
'_state': ,
'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=),
'foreign_key_id': 2,
'id': 1,
'normal_value': 1,
'readonly_value': 2}
Dies ist bei weitem das einfachste, aber es fehlt many_to_many
, foreign_key
ist falsch benannt und es hat zwei unerwünschte zusätzliche Elemente darin.
2. model_to_dict
from django.forms.models import model_to_dict
model_to_dict(instance)
was zurückgibt
{'foreign_key': 2,
'id': 1,
'many_to_many': [],
'normal_value': 1}
Dies ist das einzige mit many_to_many
, aber es fehlen die nicht-editierbaren Felder.
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])
was zurückgibt
{'foreign_key': 2, 'id': 1, 'normal_value': 1}
Dies ist streng genommen schlechter als der Standardaufruf von model_to_dict
.
4. query_set.values()
SomeModel.objects.filter(id=instance.id).values()[0]
was zurückgibt
{'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=),
'foreign_key_id': 2,
'id': 1,
'normal_value': 1,
'readonly_value': 2}
Dies ist die gleiche Ausgabe wie instance.__dict__
, aber ohne die zusätzlichen Felder. foreign_key_id
ist immer noch falsch und many_to_many
fehlt immer noch.
5. Benutzerdefinierte Funktion
Der Code für model_to_dict
von Django hatte die meisten Antworten. Er hat explizit nicht-editierbare Felder entfernt, also entfernen dieses Checks und das Abfragen der IDs von Fremdschlüsseln für viele-zu-viele-Felder führt zum folgenden Code, der wie gewünscht funktioniert:
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
Obwohl dies die komplizierteste Option ist, liefert der Aufruf von to_dict(instance)
genau das gewünschte Ergebnis:
{'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=),
'foreign_key': 2,
'id': 1,
'many_to_many': [2],
'normal_value': 1,
'readonly_value': 2}
6. Verwenden von Serialisierern
Django Rest Framework's ModelSerializer ermöglicht es Ihnen, automatisch einen Serializer aus einem Modell zu erstellen.
from rest_framework import serializers
class SomeModelSerializer(serializers.ModelSerializer):
class Meta:
model = SomeModel
fields = "__all__"
SomeModelSerializer(instance).data
liefert
{'auto_now_add': '2018-12-20T21:34:29.494827Z',
'foreign_key': 2,
'id': 1,
'many_to_many': [2],
'normal_value': 1,
'readonly_value': 2}
Dies ist fast so gut wie die benutzerdefinierte Funktion, aber auto_now_add ist eine Zeichenfolge anstelle eines Datumsobjekts.
Bonusrunde: bessere Modellanzeige
Wenn Sie ein Django-Modell wünschen, das eine bessere Python-Befehlszeilenanzeige hat, lassen Sie Ihre Modelle von dem folgenden Kind-Klasse:
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
Zum Beispiel, wenn wir unsere Modelle wie folgt definieren:
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")
Der Aufruf von SomeModel.objects.first()
gibt jetzt eine Ausgabe wie diese:
{'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=),
'foreign_key': 2,
'id': 1,
'many_to_many': [2],
'normal_value': 1,
'readonly_value': 2}