python - Konvertieren des Django Model-Objekts in dict mit allen intakten Feldern




dictionary django-models (9)

Einfachste Art,

  1. Wenn Ihre Abfrage Model.Objects.get () lautet:

    get () gibt eine einzelne Instanz zurück, so dass Sie direkt __dict__ von Ihrer Instanz verwenden können

    model_dict = Model.Objects.get().__dict__

  2. für filter () / all ():

    all () / filter () gibt eine Liste von Instanzen zurück, so dass Sie mit values() eine Liste von Objekten erhalten können.

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

Wie konvertiert man ein Django Model-Objekt in ein dict mit all seinen Feldern? Alle enthält idealerweise Fremdschlüssel und Felder mit bearbeitbaren = Falsch.

Lass mich das ausarbeiten. Nehmen wir an, ich habe ein Django-Modell wie das folgende:

from django.db import models

class OtherModel(models.Model): pass

class SomeModel(models.Model):
    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")

Im Terminal habe ich Folgendes getan:

other_model = OtherModel()
other_model.save()
instance = SomeModel()
instance.value = 1
instance.value2 = 2
instance.reference1 = other_model
instance.save()
instance.reference2.add(other_model)
instance.save()

Ich möchte dies in das folgende Wörterbuch konvertieren:

{'created': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
 u'id': 1,
 'reference1': 1,
 'reference2': [1],
 'value': 1,
 'value2': 2}

Fragen mit unbefriedigenden Antworten:

Django: Konvertiert einen vollständigen Satz von Objekten eines Modells in ein einzelnes Wörterbuch

Wie kann ich Django Model-Objekte in ein Wörterbuch umwandeln und trotzdem ihre Fremdschlüssel haben?


Nur vars(obj) , es wird die gesamten Werte des Objekts vars(obj)

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

Vielleicht hilft dir das. Möge dies nicht viele zu viele Relationen verdecken, aber es ist ziemlich praktisch, wenn Sie Ihr Modell im JSON-Format senden möchten.

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

Es gibt viele Möglichkeiten, die Instanz in ein Wörterbuch zu konvertieren, mit unterschiedlichen Graden der Fallbearbeitung und der Nähe zum gewünschten Ergebnis.

1. instance.__dict__

instance.__dict__

was zurückkommt

{'_reference1_cache': <OtherModel: OtherModel object>,
 '_state': <django.db.models.base.ModelState at 0x1f63310>,
 'created': datetime.datetime(2014, 2, 21, 4, 38, 51, 844795, tzinfo=<UTC>),
 'id': 1L,
 'reference1_id': 1L,
 'value': 1,
 'value2': 2}

Dies ist bei weitem die einfachste, aber fehlt Referenz2, Referenz1 ist falsch benannt, und es hat zwei zusätzliche Dinge darin.

2. model_to_dict

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

was zurückkommt

{u'id': 1L, 'reference1': 1L, 'reference2': [1L], 'value': 1}

Dies ist die einzige mit Referenz2, aber die nicht bearbeitbaren Felder fehlen.

3. model_to_dict mit Feldern

from django.forms.models import model_to_dict
model_to_dict(instance, fields=[field.name for field in instance._meta.fields])

was zurückkommt

{u'id': 1L, 'reference1': 1L, 'value': 1}

Dies ist streng schlechter als der Standard-Aufruf von model_to_dict.

4. query_set.values ​​()

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

was zurückkommt

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

Dies ist die gleiche Ausgabe wie die instance.__dict__ aber ohne die zusätzlichen Felder.

5. Benutzerdefinierte Funktion

Der Code für djangos model_to_dict hatte den größten Teil der Antwort. Es wurden nicht bearbeitbare Felder explizit entfernt. Wenn Sie diese Überprüfung entfernen, wird der folgende Code angezeigt, der sich wie gewünscht verhält:

from django.db.models.fields.related import ManyToManyField

def to_dict(instance):
    opts = instance._meta
    data = {}
    for f in opts.concrete_fields + opts.many_to_many:
        if isinstance(f, ManyToManyField):
            if instance.pk is None:
                data[f.name] = []
            else:
                data[f.name] = list(f.value_from_object(instance).values_list('pk', flat=True))
        else:
            data[f.name] = f.value_from_object(instance)
    return data

Obwohl dies die komplizierteste Option ist, gibt uns der Aufruf von to_dict(instance) genau das gewünschte Ergebnis:

{'created': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
 u'id': 1,
 'reference1': 1,
 'reference2': [1],
 'value': 1,
 'value2': 2}

Bonusrunde

Wenn Sie ein Django-Modell mit einer besseren Python-Befehlszeilenanzeige möchten, haben Sie folgende Child-Klasse für Ihr Modell:

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))
            else:
                data[f.name] = f.value_from_object(self)
        return data

    class Meta:
        abstract = True

Wenn wir zum Beispiel unsere Modelle als solche definieren:

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

Durch den Aufruf von SomeModel.objects.first() nun folgende Ausgabe:

{'created': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
'value': 1, 'value2': 2, 'reference1': 1, u'id': 1, 'reference2': [1]}

(wollte nicht den Kommentar abgeben)

Ok, es hängt nicht wirklich von Typen auf diese Art ab. Ich habe die ursprüngliche Frage hier vielleicht falsch verstanden, also vergib mir, wenn das der Fall ist. Wenn Sie serliazers.py erstellen, erstellen Sie Klassen mit Meta-Klassen.

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

Wenn Sie dann die Daten in der Ansichtsklasse abrufen, können Sie:

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

Das ist ziemlich viel, was ich in einer Vielzahl von Orten habe, und es gibt netter JSON über den JSONRenderer zurück.

Wie ich schon sagte, ist dies eine Bereicherung des DjangoRestFrameworks, also lohnt es sich, das zu untersuchen.


Ich habe eine saubere Lösung gefunden, um zum Ergebnis zu kommen:

Angenommen, Sie haben ein Modellobjekt o :

Ruf einfach an:

type(o).objects.filter(pk=o.pk).values().first()

@ Zags Lösung war wunderschön!

Ich würde jedoch eine Bedingung für Datumsfelder hinzufügen, um JSON-freundlich zu machen.

Bonusrunde

Wenn Sie ein Django-Modell mit einer besseren Python-Befehlszeilenanzeige möchten, haben Sie folgende Child-Klasse für Ihr Modell:

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

Wenn wir zum Beispiel unsere Modelle als solche definieren:

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

Durch den Aufruf von SomeModel.objects.first() nun folgende Ausgabe:

{'created': 1426552454.926738,
'value': 1, 'value2': 2, 'reference1': 1, u'id': 1, 'reference2': [1]}

Viele interessante Lösungen hier. Meine Lösung bestand darin, meinem Modell eine as_dict-Methode mit einem Diktatverständnis hinzuzufügen.

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

Als Bonus bietet diese Lösung zusammen mit einem Listenverständnis über eine Abfrage eine gute Lösung, wenn Sie Ihre Modelle in eine andere Bibliothek exportieren möchten. Wenn Sie beispielsweise Ihre Modelle in einen Pandas-Datenrahmen einbetten:

pandas_awesomeness = pd.DataFrame([m.as_dict() for m in SomeModel.objects.all()])

Sie können die Funktion db.rename_column verwenden.

class Migration:

    def forwards(self, orm):
        # Rename 'name' field to 'full_name'
        db.rename_column('app_foo', 'name', 'full_name')




    def backwards(self, orm):
        # Rename 'full_name' field to 'name'
        db.rename_column('app_foo', 'full_name', 'name')

Das erste Argument von db.rename_column ist der Tabellenname. db.rename_column ist es wichtig, sich daran zu erinnern, wie Django Tabellennamen erstellt :

Django leitet den Namen der Datenbanktabelle automatisch vom Namen Ihrer Modellklasse und der App, die sie enthält, ab. Der Name eines Datenbanktabels eines Modells wird erstellt, indem das "app label" des Modells - der Name, den Sie in manage.py startapp verwendet haben - mit dem Klassennamen des Modells mit einem Unterstrich verknüpft wird.

app_projectitem Sie einen Modellnamen mit mehreren Wörtern und Kamelen haben, z. B. ProjectItem, app_projectitem der Tabellenname app_projectitem (dh, ein Unterstrich wird nicht zwischen project und item eingefügt item auch wenn er mit einem Kamel versehen ist).







python django dictionary django-models