Es gibt viele Möglichkeiten, eine Instanz in ein Wörterbuch zu konvertieren, mit unterschiedlichem Grad an Behandlung von Eckfällen und Nähe zum gewünschten Ergebnis.
1. instance.__dict__
instance.__dict__
was zurückkehrt
{'_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}
Dies ist bei weitem das einfachste, fehlt aber many_to_many
, foreign_key
ist falsch benannt und enthält zwei unerwünschte zusätzliche Dinge.
2. model_to_dict
from django.forms.models import model_to_dict
model_to_dict(instance)
was zurückkehrt
{'foreign_key': 2,
'id': 1,
'many_to_many': [<OtherModel: OtherModel object>],
'normal_value': 1}
Dies ist die einzige mit many_to_many
, aber es fehlen die nicht bearbeitbaren 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ückkehrt
{'foreign_key': 2, 'id': 1, 'normal_value': 1}
Dies ist streng schlechter als der Standardaufruf model_to_dict
.
4. query_set.values()
SomeModel.objects.filter(id=instance.id).values()[0]
was zurückkehrt
{'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}
Dies ist die gleiche Ausgabe wie, instance.__dict__
jedoch 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 Djangos model_to_dict
hatte die meiste Antwort. Nicht bearbeitbare Felder wurden explizit entfernt. Wenn Sie diese Prüfung entfernen und die IDs von Fremdschlüsseln für viele bis viele Felder abrufen, erhalten Sie den folgenden Code, der sich wie gewünscht verhält:
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
Während dies die komplizierteste Option ist, to_dict(instance)
liefert der Anruf genau das gewünschte Ergebnis:
{'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. Verwenden Sie Serializer
Mit dem ModelSerialzer von Django Rest Framework können Sie einen Serializer automatisch aus einem Modell erstellen.
from rest_framework import serializers
class SomeModelSerializer(serializers.ModelSerializer):
class Meta:
model = SomeModel
fields = "__all__"
SomeModelSerializer(instance).data
kehrt zurück
{'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 datetime-Objekts.
Bonusrunde: Besserer Modelldruck
Wenn Sie ein Django-Modell mit einer besseren Python-Befehlszeilenanzeige möchten, lassen Sie Ihre Modelle wie folgt untergeordnet sein:
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
Wenn wir zum Beispiel unsere Modelle als solche 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")
Wenn Sie SomeModel.objects.first()
jetzt anrufen, erhalten Sie folgende Ausgabe:
{'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
und wie gewünscht behandeln.