Konvertieren Sie das Django-Modellobjekt in ein Diktat, bei dem alle Felder intakt sind

257

Wie konvertiert man ein Django-Modellobjekt in ein Diktat mit all seinen Feldern? Alles beinhaltet idealerweise Fremdschlüssel und Felder mit editable=False.

Lassen Sie mich näher darauf eingehen. Angenommen, ich habe ein Django-Modell wie das folgende:

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

Im Terminal habe ich Folgendes getan:

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

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

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

Fragen mit unbefriedigenden Antworten:

Django: Konvertieren eines ganzen Satzes von Objektobjekten in ein einziges Wörterbuch

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

Zags
quelle
1
Sie können eine aufgerufene Methode deklarieren to_dictund wie gewünscht behandeln.
Karthikr
@karthikr ja, ich könnte. Die Frage ist, wie man eine solche Methode erstellt. Das manuelle Erstellen eines Wörterbuchs aus allen Feldern des Modells ist keine geeignete Antwort.
Zags
Ich würde eine vorhandene ReST-Bibliothek wie Django Rest Framework, Tastypie oder Piston nutzen, da sie alle Mechanismen zum Konvertieren von Django-Modellinstanzen in Grundelemente für die Serialisierung bieten. Wenn Sie neugieriger sind, wie, können Sie den Code durchsehen, aber meistens werden die _metaDefinitionen des Modells durchgesehen , um die mit dem Modell verknüpften Felder zu finden und ihre Werte für die Instanz abzurufen.
Kevin Stone

Antworten:

525

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_keyist 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_idist immer noch falsch und many_to_manyfehlt immer noch.


5. Benutzerdefinierte Funktion

Der Code für Djangos model_to_dicthatte 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}
Zags
quelle
2
Danke für diese Antwort! Sie können den isinstanceTest in Lösung 5 (und den Bonus) in ändern if f.many_to_many.
Dhobbs
1
@dhobbs Ich habe diesen Code aus Djangos model_to_dictCode modelliert , der verwendet isinstance. Ich bin nicht sicher, warum sie diese Wahl getroffen haben, aber es kann einen guten Grund dafür geben (wie die many_to_manyEigenschaft, die in einer späteren Version eingeführt wird)
Zags
würde es auch @propertyFelder zurückgeben?
Angrysumit
1
Ich frage mich, wie diese Methoden die mit Anmerkungen versehenen / aggregierten Felder behandeln würden.
Mehmet
Ich überprüfe auf get_FOO_display und gebe diesen Wert zurück, anstatt den Wert, der tatsächlich vorhanden ist.
Bobort
9

Ich habe eine gute Lösung gefunden, um zum Ergebnis zu gelangen:

Angenommen, Sie haben ein Modellobjekt o:

Ruf einfach an:

type(o).objects.filter(pk=o.pk).values().first()
Alfred Huang
quelle
10
Dies ist nur Option # 4 in meiner Antwort
Zags
7

@ Zags Lösung war wunderschön!

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

Bonusrunde

Wenn Sie ein Django-Modell mit einer besseren Python-Befehlszeilenanzeige möchten, lassen Sie die untergeordnete Klasse Ihres Modells wie folgt lauten:

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

Wenn Sie SomeModel.objects.first()jetzt anrufen, erhalten Sie folgende Ausgabe:

{'created': 1426552454.926738,
'value': 1, 'value2': 2, 'reference1': 1, u'id': 1, 'reference2': [1]}
Diego Freitas Coelho
quelle
Wenn Sie zu und von JSON konvertieren möchten, sollten Sie sich das Django Rest Framework ansehen oder Folgendes verwenden
Zags
Sicher! Aber diese kleine Änderung an Ihrem Code bietet viel Komfort!
Diego Freitas Coelho
4

Einfachster Weg,

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

    get () gibt eine einzelne Instanz zurück, damit Sie sie direkt __dict__von Ihrer Instanz aus verwenden können

    model_dict = Model.Objects.get().__dict__

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

    all () / filter () gibt eine Liste der Instanzen zurück, damit Sie values()eine Liste der Objekte abrufen können.

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

Mohideen bin Mohammed
quelle
4

nur vars(obj)wird sie die ganzen Werte des Objekts angeben

>>> obj_attrs = vars(obj)
>>> obj_attrs
 {'_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,
 }

Sie können dies auch hinzufügen

>>> keys = obj_attrs.keys()
>>> temp = [obj_attrs.pop(key) if key.startswith('_') else None for key in keys]
>>> del temp
>>> obj_attrs
   {
    '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,
   }
A. Raouf
quelle
3

Aktualisieren

Die neuere aggregierte Antwort von @zags ist vollständiger und eleganter als meine eigene. Bitte beziehen Sie sich stattdessen auf diese Antwort.

Original

Wenn Sie bereit sind, Ihre eigene to_dict-Methode zu definieren, wie von @karthiker vorgeschlagen, führt dies dieses Problem nur zu einem Mengenproblem.

>>># Returns a set of all keys excluding editable = False keys
>>>dict = model_to_dict(instance)
>>>dict

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


>>># Returns a set of editable = False keys, misnamed foreign keys, and normal keys
>>>otherDict = SomeModel.objects.filter(id=instance.id).values()[0]
>>>otherDict

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

Wir müssen die falsch beschrifteten Fremdschlüssel aus otherDict entfernen .

Zu diesem Zweck können wir eine Schleife verwenden, die ein neues Wörterbuch erstellt, das alle Elemente außer denen mit Unterstrichen enthält. Um Zeit zu sparen, können wir diese einfach zum ursprünglichen Diktat hinzufügen, da Wörterbücher nur unter der Haube gespeichert werden.

>>>for item in otherDict.items():
...    if "_" not in item[0]:
...            dict.update({item[0]:item[1]})
...
>>>

Somit bleibt uns das folgende Diktat :

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

Und das gibst du einfach zurück.

Auf der anderen Seite können Sie keine Unterstriche in Ihren bearbeitbaren = falschen Feldnamen verwenden. Auf der anderen Seite funktioniert dies für alle Felder, in denen die vom Benutzer erstellten Felder keine Unterstriche enthalten.

Dies ist nicht der beste Weg, dies zu tun, aber es könnte als vorübergehende Lösung funktionieren, bis eine direktere Methode gefunden wird.

Für das folgende Beispiel würde dict basierend auf model_to_dict gebildet und otherDict würde durch die Filterwertmethode gebildet. Ich hätte dies mit den Modellen selbst gemacht, aber ich kann meine Maschine nicht dazu bringen, andere Modelle zu akzeptieren.

>>> import datetime
>>> dict = {u'id': 1, 'reference1': 1, 'reference2': [1], 'value': 1}
>>> otherDict = {'created': datetime.datetime(2014, 2, 21, 4, 38, 51), u'id': 1, 'reference1_id': 1, 'value': 1, 'value2': 2}
>>> for item in otherDict.items():
...     if "_" not in item[0]:
...             dict.update({item[0]:item[1]})
...
>>> dict
{'reference1': 1, 'created': datetime.datetime(2014, 2, 21, 4, 38, 51), 'value2': 2, 'value': 1, 'id': 1, 'reference2': [1]}
>>>

Das sollte Sie in einen groben Ballpark der Antwort auf Ihre Frage bringen, hoffe ich.

Gadget
quelle
1
Ich bin mir nicht sicher, wofür Sie hier verwenden remöchten. Wenn Schlüssel mit Unterstrichen herausgefiltert werden sollen, ist dies weder korrekter Code noch korrektes Verhalten. re.match("_", "reference1_id")Rückgaben Noneund legitime Spalten in der Datenbank enthalten möglicherweise Unterstriche in ihren Namen.
Zags
re.match ("_", "reference1_id") gibt None zurück, es hätte sein sollen: re.match (". * _. *", "reference1_id")
Gadget
Ich habe einige Änderungen vorgenommen, um das schlechte Beispiel zu entfernen und ein besseres aufzunehmen. Ich habe auch einige Dinge geändert, um auszudrücken, dass dies eine vorübergehende Lösung für eine Teilmenge aller Modelle wäre. Ich habe keine Ahnung, was Sie für Modelle mit Unterstrichen in ihren editable=falseBereichen tun würden . Ich habe nur versucht, etwas bereitzustellen, mit dem Sie möglicherweise arbeiten können, bis eine kanonischere Lösung geliefert werden kann.
Gadget
Vielleicht "_" in stringeher als rein diesem Fall verwenden.
Zags
Ja, das wäre viel einfacher. Es war mir nicht in den Sinn gekommen, es auf diese Weise zu benutzen, aber es macht jetzt vollkommen Sinn. Ich habe die Antwort geändert, um sie inanstelle von zu verwenden re.
Gadget
2

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 in Kombination mit einem Listenverständnis über eine Abfrage eine gute Lösung, wenn Sie Ihre Modelle in eine andere Bibliothek exportieren möchten. Beispiel: Speichern Sie Ihre Modelle in einem Pandas-Datenrahmen:

pandas_awesomeness = pd.DataFrame([m.as_dict() for m in SomeModel.objects.all()])
t1m0
quelle
1
Dies funktioniert gut für Wertefelder wie Strings und Ints, hat aber einige Probleme mit Fremdschlüsseln und noch mehr mit vielen bis vielen Feldern
Zags
Sehr guter Punkt! Besonders für viele zu viele. Man möchte einige Bedingungen einfügen, um diese Fälle angemessen zu behandeln, oder diese Lösung auf einfache Modelle beschränken. Vielen Dank.
t1m0
1

(wollte den Kommentar nicht machen)

Ok, es hängt nicht wirklich von den Typen auf diese Weise ab. Möglicherweise habe ich die ursprüngliche Frage hier falsch verstanden. Verzeihen Sie mir, wenn dies der Fall ist. Wenn Sie serliazers.py erstellen, erstellen Sie dort Klassen mit Metaklassen.

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

Wenn Sie dann die Daten in der Ansichtsklasse erhalten, 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 so ziemlich das, was ich an verschiedenen Orten habe und es gibt über den JSONRenderer einen schönen JSON zurück.

Wie gesagt, dies ist mit freundlicher Genehmigung von DjangoRestFramework, daher lohnt es sich, dies zu untersuchen.

nathj07
quelle
1

Der einfachere Weg ist, einfach zu verwenden pprint, was in Basis-Python ist

import pprint
item = MyDjangoModel.objects.get(name = 'foo')
pprint.pprint(item.__dict__, indent = 4)

Dies ergibt eine Ausgabe, die ähnlich aussieht, json.dumps(..., indent = 4)aber die seltsamen Datentypen, die möglicherweise in Ihre Modellinstanz eingebettet sind, wie z. B. ModelStateund UUIDusw., korrekt verarbeitet .

Getestet auf Python 3.7

user5359531
quelle
0

Vielleicht hilft dir das. Möge dies nicht viele bis viele Beziehungen verbergen, 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
Pjl
quelle
0

Beste Lösung, die Sie je gesehen haben.

Konvertieren Sie die Instanz django.db.models.Model und alle zugehörigen Funktionsfelder ForeignKey, ManyToManyField und @Property in dict.

"""
Convert django.db.models.Model instance and all related ForeignKey, ManyToManyField and @property function fields into dict.
Usage:
    class MyDjangoModel(... PrintableModel):
        to_dict_fields = (...)
        to_dict_exclude = (...)
        ...
    a_dict = [inst.to_dict(fields=..., exclude=...) for inst in MyDjangoModel.objects.all()]
"""
import typing

import django.core.exceptions
import django.db.models
import django.forms.models


def get_decorators_dir(cls, exclude: typing.Optional[set]=None) -> set:
    """
    Ref: /programming/4930414/how-can-i-introspect-properties-and-model-fields-in-django
    :param exclude: set or None
    :param cls:
    :return: a set of decorators
    """
    default_exclude = {"pk", "objects"}
    if not exclude:
        exclude = default_exclude
    else:
        exclude = exclude.union(default_exclude)

    return set([name for name in dir(cls) if name not in exclude and isinstance(getattr(cls, name), property)])


class PrintableModel(django.db.models.Model):

    class Meta:
        abstract = True

    def __repr__(self):
        return str(self.to_dict())

    def to_dict(self, fields: typing.Optional[typing.Iterable]=None, exclude: typing.Optional[typing.Iterable]=None):
        opts = self._meta
        data = {}

        # support fields filters and excludes
        if not fields:
            fields = set()
        else:
            fields = set(fields)
        default_fields = getattr(self, "to_dict_fields", set())
        fields = fields.union(default_fields)

        if not exclude:
            exclude = set()
        else:
            exclude = set(exclude)
        default_exclude = getattr(self, "to_dict_exclude", set())
        exclude = exclude.union(default_exclude)

        # support syntax "field__childField__..."
        self_fields = set()
        child_fields = dict()
        if fields:
            for i in fields:
                splits = i.split("__")
                if len(splits) == 1:
                    self_fields.add(splits[0])
                else:
                    self_fields.add(splits[0])

                    field_name = splits[0]
                    child_fields.setdefault(field_name, set())
                    child_fields[field_name].add("__".join(splits[1:]))

        self_exclude = set()
        child_exclude = dict()
        if exclude:
            for i in exclude:
                splits = i.split("__")
                if len(splits) == 1:
                    self_exclude.add(splits[0])
                else:
                    field_name = splits[0]
                    if field_name not in child_exclude:
                        child_exclude[field_name] = set()
                    child_exclude[field_name].add("__".join(splits[1:]))

        for f in opts.concrete_fields + opts.many_to_many:
            if self_fields and f.name not in self_fields:
                continue
            if self_exclude and f.name in self_exclude:
                continue

            if isinstance(f, django.db.models.ManyToManyField):
                if self.pk is None:
                    data[f.name] = []
                else:
                    result = []
                    m2m_inst = f.value_from_object(self)
                    for obj in m2m_inst:
                        if isinstance(PrintableModel, obj) and hasattr(obj, "to_dict"):
                            d = obj.to_dict(
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name),
                            )
                        else:
                            d = django.forms.models.model_to_dict(
                                obj,
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name)
                            )
                        result.append(d)
                    data[f.name] = result
            elif isinstance(f, django.db.models.ForeignKey):
                if self.pk is None:
                    data[f.name] = []
                else:
                    data[f.name] = None
                    try:
                        foreign_inst = getattr(self, f.name)
                    except django.core.exceptions.ObjectDoesNotExist:
                        pass
                    else:
                        if isinstance(foreign_inst, PrintableModel) and hasattr(foreign_inst, "to_dict"):
                            data[f.name] = foreign_inst.to_dict(
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name)
                            )
                        elif foreign_inst is not None:
                            data[f.name] = django.forms.models.model_to_dict(
                                foreign_inst,
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name),
                            )

            elif isinstance(f, (django.db.models.DateTimeField, django.db.models.DateField)):
                v = f.value_from_object(self)
                if v is not None:
                    data[f.name] = v.isoformat()
                else:
                    data[f.name] = None
            else:
                data[f.name] = f.value_from_object(self)

        # support @property decorator functions
        decorator_names = get_decorators_dir(self.__class__)
        for name in decorator_names:
            if self_fields and name not in self_fields:
                continue
            if self_exclude and name in self_exclude:
                continue

            value = getattr(self, name)
            if isinstance(value, PrintableModel) and hasattr(value, "to_dict"):
                data[name] = value.to_dict(
                    fields=child_fields.get(name),
                    exclude=child_exclude.get(name)
                )
            elif hasattr(value, "_meta"):
                # make sure it is a instance of django.db.models.fields.Field
                data[name] = django.forms.models.model_to_dict(
                    value,
                    fields=child_fields.get(name),
                    exclude=child_exclude.get(name),
                )
            elif isinstance(value, (set, )):
                data[name] = list(value)
            else:
                data[name] = value

        return data

https://gist.github.com/shuge/f543dc2094a3183f69488df2bfb51a52

Tony
quelle
0

Die Antwort von @zags ist umfassend und sollte ausreichen, aber die Methode Nr. 5 (die beste IMO) gibt einen Fehler aus, sodass ich die Hilfsfunktion verbessert habe.

Da das OP die Konvertierung von many_to_manyFeldern in eine Liste von Primärschlüsseln anstelle einer Liste von Objekten anforderte , habe ich die Funktion erweitert, sodass der Rückgabewert jetzt JSON-serialisierbar ist - indem datetimeObjekte in strund many_to_manyObjekte in eine Liste von IDs konvertiert werden .

import datetime

def ModelToDict(instance):
    '''
    Returns a dictionary object containing complete field-value pairs of the given instance

    Convertion rules:

        datetime.date --> str
        many_to_many --> list of id's

    '''

    concrete_fields = instance._meta.concrete_fields
    m2m_fields = instance._meta.many_to_many
    data = {}

    for field in concrete_fields:
        key = field.name
        value = field.value_from_object(instance)
        if type(value) == datetime.datetime:
            value = str(field.value_from_object(instance))
        data[key] = value

    for field in m2m_fields:
        key = field.name
        value = field.value_from_object(instance)
        data[key] = [rel.id for rel in value]

    return data
Armin Hemati Nik
quelle
Was ist der Fehler, den Sie bekommen? Ich freue mich, die Antwort zu aktualisieren
Zags
Derzeit ist die Funktionalität der Schleifen durch concrete_fieldsund m2m_fieldssieht identisch aus. Angenommen, die m2m_fieldsSchleife ist hier falsch implementiert.
Daniel Himmelstein
@Zags der Fehler ist, AttributeError: 'list' object has no attribute 'values_list' dass ich den Grund dafür nicht finden konnte.
Ich
@ daniel-himmelstein danke für den hinweis, ich habe den code korrigiert. Der Grund für identische Schleifen lag in der Ausführung unterschiedlicher Operationen in meinem lokalen Code, und ich habe vergessen, ihn für die SO-Antwort zu optimieren.
Armin Hemati Nik
@ArminHemati Django hat die Implementierung von field.value_from_objectund infolgedessen von geändert model_to_dict. Ich habe Abschnitt 5 meiner Antwort aktualisiert, um dies widerzuspiegeln.
Zags