Konwertuj obiekt Django Model na dyktowanie z nienaruszonymi wszystkimi polami


257

Jak można przekonwertować obiekt Django Model na dyktowanie za pomocą wszystkimi jego polami? Wszystko idealnie zawiera klucze obce i pola z editable=False.

Pozwól mi rozwinąć. Powiedzmy, że mam model Django, taki jak poniżej:

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

W terminalu wykonałem następujące czynności:

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

Chcę przekonwertować to na następujący słownik:

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

Pytania z niezadowalającymi odpowiedziami:

Django: Konwertowanie całego zestawu obiektów Modelu na pojedynczy słownik

Jak mogę przekształcić obiekty Django Model w słownik i nadal mieć ich klucze obce?


1
możesz zadeklarować wywołaną metodę to_dicti obsłużyć ją tak, jak chcesz.
karthikr

@karthikr tak, mógłbym. Pytanie brzmi, jak stworzyć taką metodę. Ręczne konstruowanie słownika ze wszystkich pól modelu nie jest odpowiednią odpowiedzią.
Zags

Wykorzystałbym istniejącą bibliotekę ReST, taką jak Django Rest Framework, Tastypie lub Piston, ponieważ wszystkie one zapewniają mechanizmy przekształcania instancji modelu django w operacje podstawowe w celu serializacji. Jeśli jesteś ciekaw, jak możesz to zrobić, możesz przejrzeć ich kod, ale głównie chodzi o _metadefinicje modelu, aby znaleźć pola powiązane z modelem i pobrać ich wartości w instancji.
Kevin Stone

Odpowiedzi:


523

Istnieje wiele sposobów konwersji instancji na słownik o różnym stopniu obsługi przypadków w narożnikach i bliskości pożądanego wyniku.


1. instance.__dict__

instance.__dict__

który zwraca

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

Jest to zdecydowanie najprostsze, ale brakuje go many_to_many, foreign_keyjest źle nazwane i zawiera dwie niepożądane rzeczy dodatkowe.


2) model_to_dict

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

który zwraca

{'foreign_key': 2,
 'id': 1,
 'many_to_many': [<OtherModel: OtherModel object>],
 'normal_value': 1}

Jest to jedyny z many_to_many, ale brakuje pól nieedytowalnych.


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

który zwraca

{'foreign_key': 2, 'id': 1, 'normal_value': 1}

Jest to zdecydowanie gorsze niż standardowe model_to_dictwywołanie.


4 query_set.values()

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

który zwraca

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

Jest to ten sam wynik, instance.__dict__ale bez dodatkowych pól. foreign_key_idjest nadal błędny i many_to_manynadal go brakuje.


5. Funkcja niestandardowa

Kod dla django model_to_dictmiał większość odpowiedzi. Wyraźnie usunął pola nieedytowalne, więc usunięcie tej kontroli i pobranie identyfikatorów kluczy obcych dla wielu do wielu pól skutkuje następującym kodem, który zachowuje się zgodnie z potrzebami:

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

Chociaż jest to najbardziej skomplikowana opcja, wywoływanie to_dict(instance)daje nam dokładnie pożądany rezultat:

{'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. Użyj serializatorów

ModelSerialzer w Django Rest Framework umożliwia automatyczne tworzenie serializatora z modelu.

from rest_framework import serializers
class SomeModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = SomeModel
        fields = "__all__"

SomeModelSerializer(instance).data

zwroty

{'auto_now_add': '2018-12-20T21:34:29.494827Z',
 'foreign_key': 2,
 'id': 1,
 'many_to_many': [2],
 'normal_value': 1,
 'readonly_value': 2}

Jest to prawie tak dobre, jak funkcja niestandardowa, ale auto_now_add jest ciągiem zamiast obiektu datetime.


Runda bonusowa: lepsze drukowanie modelu

Jeśli chcesz mieć model Django z lepszym wyświetlaniem wiersza polecenia Pythona, poproś swoje modele o klasę potomną:

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

Na przykład, jeśli zdefiniujemy nasze modele jako takie:

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

Wywołanie SomeModel.objects.first()teraz daje wynik w następujący sposób:

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

2
Dzięki za tę odpowiedź! Możesz zmienić isinstancetest w rozwiązaniu nr 5 (i premię) na if f.many_to_many.
dhobbs

1
@dhobbs Modelowałem ten kod na podstawie kodu Django model_to_dict, który wykorzystuje isinstance. Nie jestem pewien, dlaczego dokonali tego wyboru, ale może istnieć dobry powód (na przykład wprowadzenie many_to_manynieruchomości w późniejszej wersji)
Zags

czy zwróci również @propertypola?
angrysumit

1
Zastanawiam się, jak te metody potraktowałyby pola z adnotacjami / agregacją?
mehmet,

Coś, co robię, to sprawdza get_FOO_display i zwraca tę wartość zamiast jakiejkolwiek wartości, która faktycznie może tam być.
Bobort

9

Znalazłem dobre rozwiązanie, aby uzyskać wynik:

Załóżmy, że masz obiekt modelu o:

Zadzwoń:

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

10
To tylko opcja 4 w mojej odpowiedzi
Zags

7

Rozwiązanie @Zags było wspaniałe!

Dodałbym jednak warunek dla pól daty, aby JSON był przyjazny.

Runda bonusowa

Jeśli chcesz mieć model django z lepszym wyświetlaniem wiersza polecenia Pythona, poproś klasę potomną modeli, aby:

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

Na przykład, jeśli zdefiniujemy nasze modele jako takie:

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

Wywołanie SomeModel.objects.first()teraz daje wynik w następujący sposób:

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

Jeśli chcesz przekonwertować do iz JSON, powinieneś zajrzeć do Django Rest Framework lub użyć czegoś takiego: stackoverflow.com/a/22238613/2800876
Zags

Pewnie! Ale ta niewielka zmiana w kodzie zapewnia dużą wygodę!
Diego Freitas Coelho,

4

Najprostszy sposób

  1. Jeśli twoje zapytanie to Model.Objects.get ():

    get () zwróci pojedynczą instancję, abyś mógł bezpośrednio __dict__z niej korzystać

    model_dict = Model.Objects.get().__dict__

  2. dla filter () / all ():

    all () / filter () zwróci listę instancji, dzięki czemu można użyć values()do uzyskania listy obiektów.

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


4

po prostu vars(obj)poda całe wartości obiektu

>>> 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,
 }

Możesz to również dodać

>>> 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,
   }

3

Aktualizacja

Nowsza zbiorcza odpowiedź opublikowana przez @zags jest bardziej kompletna i elegancka niż moja własna. Zamiast tego zapoznaj się z tą odpowiedzią.

Oryginalny

Jeśli chcesz zdefiniować własną metodę to_dict, jak sugerował @karthiker, to sprowadza się do problemu z zestawem.

>>># 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}

Musimy usunąć źle oznakowane klucze obce z otherDict .

Aby to zrobić, możemy użyć pętli, która tworzy nowy słownik, który zawiera każdy element z wyjątkiem tych z podkreśleniami. Lub, aby zaoszczędzić czas, możemy po prostu dodać tych, do oryginalnego dict od słowniki są tylko zestawy pod maską.

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

Pozostaje nam zatem następujący dyktand :

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

I właśnie to zwrócisz.

Z drugiej strony nie można używać podkreślników w edytowalnych = fałszywych nazwach pól. Z drugiej strony będzie to działać dla każdego zestawu pól, w których pola utworzone przez użytkownika nie zawierają podkreślników.

To nie jest najlepszy sposób na zrobienie tego, ale może działać jako rozwiązanie tymczasowe, dopóki nie zostanie znaleziona bardziej bezpośrednia metoda.

W poniższym przykładzie dict utworzono by na podstawie modelu_to_dict, a otherDict utworzono by metodą wartości filtru. Zrobiłbym to z samymi modelami, ale nie mogę zmusić mojej maszyny do zaakceptowania innego Modelu.

>>> 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]}
>>>

Mam nadzieję, że powinno to postawić cię w trudnym polu odpowiedzi na twoje pytanie.


1
Nie wiesz, do czego chcesz tutaj użyć re. Jeśli ma odfiltrować klucze z podkreśleniami, nie jest to ani poprawny kod, ani prawidłowe zachowanie. re.match("_", "reference1_id")Zwroty Nonei uzasadnione kolumny w bazie danych mogą zawierać podkreślenia w swoich nazwach.
Zags,

re.match („_”, „reference1_id”) zwraca Brak, powinno być: re.match („. * _. *”, „reference1_id”)
Gadżet

Wprowadziłem kilka zmian, aby usunąć zły przykład i dołączyć lepszy. Zmieniłem też niektóre rzeczy, aby wyrazić, że byłoby to tymczasowe rozwiązanie dla podzbioru wszystkich modeli. Nie mam pojęcia, co byś zrobił dla modeli z podkreśleniami w swoich editable=falsedziedzinach. Chciałem tylko dostarczyć coś, z czym możesz pracować, dopóki nie uda się dostarczyć więcej rozwiązania kanonicznego.
Gadżet

Może użyj "_" in stringraczej niż rew takim przypadku.
Zags,

Tak, byłby to o wiele łatwiejszy sposób. Nie przyszło mi do głowy, aby używać go w ten sposób, ale teraz ma to całkiem sensowny sens. Zmieniłem odpowiedź, aby użyć inzamiast re.
Gadżet

2

Wiele ciekawych rozwiązań tutaj. Moim rozwiązaniem było dodanie metody as_dict do mojego modelu ze zrozumieniem dict.

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

Jako bonus, to rozwiązanie w połączeniu ze zrozumieniem listy nad zapytaniem stanowi dobre rozwiązanie, jeśli chcesz wyeksportować swoje modele do innej biblioteki. Na przykład zrzuć swoje modele do ramki danych pandy:

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

1
Działa to dobrze w przypadku pól wartości, takich jak ciągi i ints, ale będzie mieć pewne problemy z kluczami obcymi, a nawet więcej z wieloma do wielu pól
Zags,

Bardzo dobra uwaga! Specjalnie dla wielu do wielu. Chciałoby się wprowadzić pewne warunki warunkowe, aby odpowiednio obsługiwać te przypadki, lub ograniczyć to rozwiązanie do prostych modeli. Dzięki.
t1m0,

1

(nie chciałem komentować)

Ok, to tak naprawdę nie zależy od typów. Być może źle zrozumiałem pierwotne pytanie, więc wybacz mi, jeśli tak jest. Jeśli tworzysz serliazers.py, tam tworzysz klasy, które mają meta-klasy.

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

Następnie, gdy uzyskasz dane w klasie widoku, możesz:

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

To jest prawie to, co mam w różnych miejscach i zwraca ładny JSON przez JSONRenderer.

Jak powiedziałem, jest to dzięki uprzejmości DjangoRestFramework, więc warto się temu przyjrzeć.


1

Najłatwiej jest po prostu użyć pprint, który znajduje się w podstawowym języku Python

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

To daje wynik, który wygląda podobnie, json.dumps(..., indent = 4)ale poprawnie obsługuje dziwne typy danych, które mogą być osadzone w instancji modelu, takie jak ModelStatei UUIDitp.

Testowane na Pythonie 3.7


0

Może to ci pomoże. Niech to nie ukrywa relacji wielu do wielu, ale jest to bardzo przydatne, gdy chcesz wysłać swój model w formacie json.

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

0

Najlepsze rozwiązanie, jakie kiedykolwiek widziałeś.

Konwertuj instancję django.db.models.Model i wszystkie powiązane pola funkcji ForeignKey, ManyToManyField i @Property na 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


0

Odpowiedź z @zags jest wyczerpująca i powinna wystarczyć, ale metoda nr 5 (najlepsza IMO) generuje błąd, więc poprawiłem funkcję pomocnika.

Ponieważ OP poprosił o konwersję many_to_manypól na listę kluczy podstawowych zamiast na listę obiektów, ulepszyłem tę funkcję, aby zwracana wartość była teraz możliwa do serializacji JSON - poprzez konwersję datetimeobiektów na stri many_to_manyobiekty na listę identyfikatorów.

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

Jaki masz błąd? Z przyjemnością aktualizuję odpowiedź
Zags

Obecnie funkcjonalność pętli concrete_fieldsi m2m_fieldswyglądają identycznie, więc zakładając, że m2m_fieldspętla ma tutaj niepoprawną implementację.
Daniel Himmelstein

@Zags wystąpił błąd, AttributeError: 'list' object has no attribute 'values_list' którego nie mogłem znaleźć. Używam Django 2.1.1
Armin Hemati Nik

@ Daniel-himmelstein dziękuję za wskazanie, naprawiłem kod. przyczyną identycznych pętli było wykonywanie różnych operacji w moim lokalnym kodzie i zapomniałem zoptymalizować go dla odpowiedzi SO.
Armin Hemati Nik

@ArminHemati Django zmienił implementację field.value_from_objectiw rezultacie model_to_dict. Zaktualizowałem sekcję 5 mojej odpowiedzi, aby to odzwierciedlić.
Zags,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.