Konversi objek Model Django ke dikt dengan semua bidang utuh

258

Bagaimana seseorang mengonversi objek Model Django ke dict dengan semua bidangnya? Semua idealnya menyertakan kunci dan bidang asing dengan editable=False.

Biarkan saya uraikan. Katakanlah saya memiliki model Django seperti berikut:

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

Di terminal, saya telah melakukan hal berikut:

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

Saya ingin mengonversi ini ke kamus berikut:

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

Pertanyaan dengan jawaban yang tidak memuaskan:

Django: Mengubah seluruh set objek Model menjadi satu kamus

Bagaimana saya bisa mengubah objek Model Django menjadi kamus dan masih memiliki kunci asing mereka?

Zags
sumber
1
Anda dapat mendeklarasikan metode yang dipanggil to_dictdan menanganinya seperti yang Anda inginkan.
karthikr
@arthikr ya, saya bisa. Pertanyaannya adalah bagaimana membuat metode seperti itu. Membuat kamus secara manual dari semua bidang model bukanlah jawaban yang cocok.
Zags
Saya akan memanfaatkan pustaka ReST yang ada seperti Django Rest Framework, Tastypie atau Piston karena semuanya menyediakan mekanisme untuk mengubah contoh model Django menjadi primitif untuk serialisasi. Jika Anda lebih penasaran, Anda bisa melihat-lihat kodenya, tetapi kebanyakan berjalan melalui _metadefinisi model untuk menemukan bidang yang terkait dengan model dan mengambil nilai-nilai mereka pada instance.
Kevin Stone

Jawaban:

526

Ada banyak cara untuk mengkonversi instance ke kamus, dengan berbagai tingkat penanganan kasus sudut dan kedekatan dengan hasil yang diinginkan.


1. instance.__dict__

instance.__dict__

yang kembali

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

Sejauh ini, ini adalah yang paling sederhana, tetapi tidak ada many_to_many, foreign_keysalah nama, dan ada dua hal tambahan yang tidak diinginkan di dalamnya.


2. model_to_dict

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

yang kembali

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

Ini adalah satu-satunya yang ada many_to_many, tetapi tidak memiliki bidang yang tidak dapat diedit.


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

yang kembali

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

Ini benar-benar lebih buruk daripada model_to_dictdoa standar .


4. query_set.values()

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

yang kembali

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

Ini adalah output yang sama seperti instance.__dict__tetapi tanpa bidang tambahan. foreign_key_idmasih salah dan many_to_manymasih hilang.


5. Fungsi Kustom

Kode untuk Django model_to_dictmemiliki sebagian besar jawabannya. Itu secara eksplisit menghapus bidang yang tidak dapat diedit, jadi menghapus centang itu dan mendapatkan id kunci asing untuk banyak ke banyak bidang menghasilkan kode berikut yang berperilaku seperti yang diinginkan:

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

Meskipun ini adalah opsi yang paling rumit, panggilan to_dict(instance)memberi kami hasil yang diinginkan:

{'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. Gunakan Serializers

ModelSerialzer Django Rest Framework memungkinkan Anda membuat serializer secara otomatis dari suatu model.

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

SomeModelSerializer(instance).data

kembali

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

Ini hampir sebagus fungsi kustom, tetapi auto_now_add adalah string, bukan objek datetime.


Putaran Bonus: pencetakan model yang lebih baik

Jika Anda menginginkan model Django yang memiliki tampilan baris perintah python yang lebih baik, mintalah kelas anak Anda model berikut:

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

Jadi, misalnya, jika kita mendefinisikan model kita seperti itu:

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

Panggilan SomeModel.objects.first()sekarang memberikan hasil seperti ini:

{'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
sumber
2
Terima kasih atas jawaban ini! Anda dapat mengubah isinstancetes dalam solusi # 5 (dan bonus) menjadi if f.many_to_many.
dhobbs
1
@ Dhobbs Saya memodelkan kode itu dari kode Django model_to_dict, yang menggunakan isinstance. Saya tidak yakin mengapa mereka membuat pilihan ini, tetapi mungkin ada alasan bagus untuk itu (seperti many_to_manyproperti yang diperkenalkan dalam versi yang lebih baru)
Zags
apakah itu juga mengembalikan @propertybidang?
angrysumit
1
Saya bertanya-tanya bagaimana metode ini memperlakukan bidang beranotasi / agregat?
mehmet
Sesuatu yang saya lakukan adalah memeriksa get_FOO_display dan mengembalikan nilai itu daripada nilai apa pun yang mungkin benar-benar ada.
Bobort
9

Saya menemukan solusi yang rapi untuk mendapatkan hasil:

Misalkan Anda memiliki objek model o:

Panggil saja:

type(o).objects.filter(pk=o.pk).values().first()
Alfred Huang
sumber
10
Ini hanya opsi # 4 dalam jawaban saya
Zags
7

Solusi Zags sangat bagus!

Saya akan menambahkan, bagaimanapun, suatu kondisi untuk datefields agar JSON friendly.

Putaran Bonus

Jika Anda menginginkan model Django yang memiliki tampilan baris perintah python yang lebih baik, mintalah kelas anak model Anda sebagai berikut:

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

Jadi, misalnya, jika kita mendefinisikan model kita seperti itu:

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

Panggilan SomeModel.objects.first()sekarang memberikan hasil seperti ini:

{'created': 1426552454.926738,
'value': 1, 'value2': 2, 'reference1': 1, u'id': 1, 'reference2': [1]}
Diego Freitas Coelho
sumber
Jika Anda ingin mengonversi ke dan dari JSON, Anda harus melihat ke Django Rest Framework atau menggunakan sesuatu seperti ini: stackoverflow.com/a/22238613/2800876
Zags
Tentu! Tetapi perubahan kecil pada kode Anda ini menambah banyak kenyamanan!
Diego Freitas Coelho
4

Cara paling sederhana,

  1. Jika kueri Anda Model.Objects.get ():

    get () akan mengembalikan satu instance sehingga Anda dapat mengarahkan penggunaan __dict__dari instance Anda

    model_dict = Model.Objects.get().__dict__

  2. untuk filter () / semua ():

    all () / filter () akan mengembalikan daftar instance sehingga Anda dapat menggunakan values()untuk mendapatkan daftar objek.

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

Mohideen bin Mohammed
sumber
4

hanya vars(obj), ini akan menyatakan seluruh nilai objek

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

Anda dapat menambahkan ini juga

>>> 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
sumber
3

Memperbarui

Jawaban teragregasi yang lebih baru yang dikirim oleh @zags lebih lengkap dan elegan daripada jawaban saya. Silakan merujuk ke jawaban itu sebagai gantinya.

Asli

Jika Anda ingin mendefinisikan metode to_dict Anda sendiri seperti yang disarankan @karthiker, maka itu hanya akan memunculkan masalah ini menjadi masalah kumpulan.

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

Kita perlu menghapus kunci asing yang salah label dari otherDict .

Untuk melakukan ini, kita dapat menggunakan loop yang membuat kamus baru yang memiliki setiap item kecuali yang bergaris bawah di dalamnya. Atau, untuk menghemat waktu, kita bisa menambahkannya ke dikt asli karena kamus hanya diset di bawah tenda.

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

Jadi kita dibiarkan dengan dikt berikut :

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

Dan Anda hanya mengembalikannya.

Pada sisi negatifnya, Anda tidak dapat menggunakan garis bawah pada nama bidang Anda yang dapat diedit = salah. Pada terbalik, ini akan berfungsi untuk set bidang mana pun di mana bidang buatan pengguna tidak mengandung garis bawah.

Ini bukan cara terbaik untuk melakukan ini, tetapi ini bisa berfungsi sebagai solusi sementara sampai metode yang lebih langsung ditemukan.

Untuk contoh di bawah ini, dict akan dibentuk berdasarkan model_to_dict dan otherDict akan dibentuk oleh metode nilai filter. Saya akan melakukan ini dengan model itu sendiri, tetapi saya tidak bisa mendapatkan mesin saya untuk menerima Model lain.

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

Itu seharusnya menempatkan Anda pada kasarnya jawaban atas pertanyaan Anda, saya harap.

Dawai
sumber
1
Tidak yakin apa yang Anda coba gunakan reuntuk di sini. Jika ingin menyaring kunci dengan garis bawah di dalamnya, ini bukan kode yang benar atau perilaku yang benar. re.match("_", "reference1_id")kembali Nonedan kolom yang sah dalam database mungkin memiliki garis bawah dalam nama mereka.
Zags
re.match ("_", "reference1_id") tidak mengembalikan Tidak ada, itu seharusnya: re.match (". * _. *", "Reference1_id")
Gadget
Saya membuat beberapa perubahan untuk menghapus contoh yang buruk dan memasukkan yang lebih baik. Saya juga mengubah beberapa hal untuk menyatakan bahwa ini akan menjadi solusi sementara untuk subset dari semua model. Saya tidak tahu apa yang akan Anda lakukan untuk model dengan garis bawah di editable=falsebidangnya. Saya hanya mencoba memberikan sesuatu yang mungkin dapat Anda kerjakan sampai solusi yang lebih kanon dapat disampaikan.
Gadget
Mungkin menggunakan "_" in stringdaripada redalam kasus itu.
Zags
Ya, itu akan menjadi cara yang jauh lebih mudah untuk melakukannya. Tidak terpikir oleh saya untuk menggunakannya dengan cara ini, tetapi masuk akal sekarang. Saya telah mengubah jawaban untuk digunakan insebagai gantinya re.
Gadget
2

Banyak solusi menarik di sini. Solusi saya adalah menambahkan metode as_dict ke model saya dengan pemahaman dict.

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

Sebagai bonus, solusi ini dipasangkan dengan pemahaman daftar atas permintaan membuat solusi yang bagus jika Anda ingin mengekspor model Anda ke perpustakaan lain. Misalnya, membuang model Anda ke dalam kerangka data panda:

pandas_awesomeness = pd.DataFrame([m.as_dict() for m in SomeModel.objects.all()])
t1m0
sumber
1
Ini berfungsi dengan baik untuk bidang nilai seperti string dan int, tetapi akan memiliki beberapa masalah dengan kunci asing dan bahkan lebih banyak dengan banyak bidang
Zags
Poin yang sangat bagus! Terutama bagi banyak orang. Seseorang ingin menempatkan beberapa persyaratan untuk menangani kasus-kasus tersebut secara tepat, atau membatasi solusi ini untuk model-model sederhana. Terima kasih.
t1m0
1

(tidak bermaksud berkomentar)

Ok, itu tidak benar-benar tergantung pada jenis dengan cara itu. Saya mungkin salah mengerti pertanyaan asli di sini, jadi maafkan saya jika itu masalahnya. Jika Anda membuat serliazers.py maka di sana Anda membuat kelas yang memiliki kelas meta.

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

Kemudian ketika Anda mendapatkan data di kelas tampilan Anda bisa:

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

Itulah yang saya miliki di berbagai tempat dan mengembalikan JSON yang bagus melalui JSONRenderer.

Seperti yang saya katakan ini adalah milik DjangoRestFramework, jadi ada baiknya melihat ke dalamnya.

nathj07
sumber
1

Cara yang lebih mudah adalah dengan menggunakan pprint, yang ada di basis Python

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

Ini memberikan output yang terlihat mirip dengan json.dumps(..., indent = 4)tetapi dengan benar menangani tipe data aneh yang mungkin tertanam dalam contoh model Anda, seperti ModelStatedan UUID, dll.

Diuji dengan Python 3.7

pengguna5359531
sumber
0

Mungkin ini membantu Anda. Mungkin ini tidak banyak hubungan rahasia ke banyak, tetapi sangat berguna ketika Anda ingin mengirim model Anda dalam format 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
Pjl
sumber
0

Solusi terbaik yang pernah Anda lihat.

Ubah instance django.db.models.Model dan semua bidang fungsi ForeignKey, ManyToManyField, dan @Property yang terkait menjadi 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
sumber
0

Jawaban dari @zags komprehensif dan harus cukup tetapi metode # 5 (yang merupakan IMO terbaik) melempar kesalahan jadi saya meningkatkan fungsi helper.

Sebagai OP yang diminta untuk mengubah many_to_manybidang menjadi daftar kunci utama daripada daftar objek, saya meningkatkan fungsi sehingga nilai kembali sekarang JSON serializable - dengan mengubah datetimeobjek menjadi strdan many_to_manyobjek ke daftar id.

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
sumber
Apa kesalahan yang Anda dapatkan? Saya senang memperbarui jawabannya
Zag
Saat ini fungsionalitas loop melalui concrete_fieldsdan m2m_fieldsterlihat identik, jadi dengan asumsi m2m_fieldsloop memiliki implementasi yang salah di sini.
Daniel Himmelstein
@ Zags kesalahannya adalah AttributeError: 'list' object has no attribute 'values_list' saya tidak bisa menemukan alasan di baliknya. Saya menggunakan Django 2.1.1
Armin Hemati Nik
@ daniel-himmelstein terima kasih telah menunjukkan, saya memperbaiki kode. alasan untuk loop identik adalah karena melakukan operasi yang berbeda dalam kode lokal saya dan saya lupa untuk mengoptimalkannya untuk jawaban SO.
Armin Hemati Nik
@ArminHemati Django mengubah implementasi field.value_from_objectdan sebagai hasilnya, dari model_to_dict. Saya telah memperbarui bagian 5 dari jawaban saya untuk mencerminkan hal ini.
Zags