Chuyển đổi đối tượng Django Model thành dict với tất cả các trường còn nguyên


258

Làm thế nào để chuyển đổi một đối tượng Mô hình Django thành một dict với tất cả các trường của nó? Tất cả lý tưởng bao gồm các khóa ngoại và trường với editable=False.

Hãy để tôi giải thích. Hãy nói rằng tôi có một mô hình Django như sau:

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

Trong thiết bị đầu cuối, tôi đã làm như sau:

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

Tôi muốn chuyển đổi từ này sang từ điển sau:

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

Câu hỏi với câu trả lời không thỏa đáng:

Django: Chuyển đổi toàn bộ tập hợp các đối tượng của Model thành một từ điển duy nhất

Làm cách nào tôi có thể biến các đối tượng Mô hình Django thành từ điển mà vẫn có khóa ngoại?


1
bạn có thể khai báo một phương thức được gọi to_dictvà xử lý nó theo cách bạn muốn.
karthikr

@karthikr vâng, tôi có thể. Câu hỏi là làm thế nào để tạo ra một phương pháp như vậy. Tự tay xây dựng một từ điển từ tất cả các lĩnh vực của mô hình không phải là một câu trả lời phù hợp.
Zags

Tôi tận dụng một thư viện ReST hiện có như Django Rest Framework, Tastypie hoặc Piston vì tất cả chúng đều cung cấp các cơ chế để chuyển đổi các thể hiện mô hình django thành nguyên thủy để tuần tự hóa. Nếu bạn tò mò hơn về cách thức, bạn có thể xem qua mã của họ, nhưng chủ yếu là _metatìm hiểu các định nghĩa của mô hình để tìm các trường được liên kết với mô hình và lấy các giá trị của chúng trên ví dụ.
Kevin Stone

Câu trả lời:


526

Có nhiều cách để chuyển đổi một thể hiện thành một từ điển, với mức độ xử lý tình huống góc khác nhau và sự gần gũi với kết quả mong muốn.


1. instance.__dict__

instance.__dict__

trả về

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

Điều này cho đến nay là đơn giản nhất, nhưng bị thiếu many_to_many, foreign_keyđược đặt tên sai, và nó có hai điều bổ sung không mong muốn trong đó.


2. model_to_dict

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

trả về

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

Đây là người duy nhất có many_to_many, nhưng thiếu các trường không thể chỉnh sửa.


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

trả về

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

Điều này là hoàn toàn tồi tệ hơn so với model_to_dictlời mời tiêu chuẩn .


4. query_set.values()

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

trả về

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

Đây là đầu ra tương tự instance.__dict__nhưng không có các trường bổ sung. foreign_key_idvẫn còn sai và many_to_manyvẫn còn thiếu


5. Chức năng tùy chỉnh

Mã cho django model_to_dictđã có hầu hết các câu trả lời. Nó loại bỏ rõ ràng các trường không thể chỉnh sửa, do đó, việc xóa kiểm tra và nhận id của khóa ngoại đối với nhiều trường dẫn đến mã sau đây hoạt động như mong muốn:

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

Mặc dù đây là tùy chọn phức tạp nhất, việc gọi to_dict(instance)cho chúng tôi chính xác kết quả mong muốn:

{'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. Sử dụng nối tiếp

ModelSerialzer của Django Rest Framework cho phép bạn tự động xây dựng bộ nối tiếp từ một mô hình.

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

SomeModelSerializer(instance).data

trả lại

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

Điều này gần như tốt như hàm tùy chỉnh, nhưng auto_now_add là một chuỗi thay vì một đối tượng datetime.


Vòng thưởng: in mô hình tốt hơn

Nếu bạn muốn một mô hình django có màn hình dòng lệnh python tốt hơn, hãy tạo các mô hình con của bạn như sau:

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

Vì vậy, ví dụ, nếu chúng ta định nghĩa các mô hình của mình như sau:

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

Gọi SomeModel.objects.first()ngay bây giờ cho đầu ra như thế này:

{'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
Cảm ơn câu trả lời này! Bạn có thể thay đổi isinstancebài kiểm tra trong giải pháp số 5 (và phần thưởng) thành if f.many_to_many.
dhobbs

1
@dhobbs Tôi đã mô hình hóa mã đó ra khỏi mã của Django model_to_dict, sử dụng isinstance. Tôi không chắc tại sao họ lại đưa ra lựa chọn này nhưng có thể có một lý do chính đáng cho nó (chẳng hạn như many_to_manytài sản được giới thiệu trong phiên bản mới hơn)
Zags

nó cũng sẽ trả về @propertycác lĩnh vực?
angrysumit

1
Tôi tự hỏi làm thế nào những phương pháp này sẽ xử lý các trường chú thích / tổng hợp?
mehmet

Một cái gì đó tôi làm là kiểm tra get_FOO_display và trả về giá trị đó thay vì bất kỳ giá trị nào thực sự có thể ở đó.
Bobort

9

Tôi tìm thấy một giải pháp gọn gàng để đi đến kết quả:

Giả sử bạn có một đối tượng mô hình o:

Chỉ cần gọi:

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

10
Đây chỉ là tùy chọn số 4 trong câu trả lời của tôi
Zags

7

Giải pháp @Zags thật tuyệt vời!

Tuy nhiên, tôi sẽ thêm một điều kiện cho các trường ngày để làm cho nó thân thiện với JSON.

Vòng thưởng

Nếu bạn muốn một mô hình django có hiển thị dòng lệnh python tốt hơn, hãy yêu cầu lớp mô hình con của bạn như sau:

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

Vì vậy, ví dụ, nếu chúng ta định nghĩa các mô hình của mình như sau:

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

Gọi SomeModel.objects.first()ngay bây giờ cho đầu ra như thế này:

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

Nếu bạn muốn chuyển đổi sang và từ JSON, bạn nên xem xét Django Rest Framework hoặc sử dụng một cái gì đó như thế này: stackoverflow.com/a/22238613/2800876
Zags

Chắc chắn rồi! Nhưng thay đổi nhỏ này để mã của bạn thêm rất nhiều thuận tiện!
Diego Freitas Coelho

4

Cách đơn giản nhất,

  1. Nếu truy vấn của bạn là Model.Objects.get ():

    get () sẽ trả về một thể hiện duy nhất để bạn có thể sử dụng trực tiếp __dict__từ cá thể của bạn

    model_dict = Model.Objects.get().__dict__

  2. cho bộ lọc () / all ():

    all () / filter () sẽ trả về danh sách các thể hiện để bạn có thể sử dụng values()để lấy danh sách các đối tượng.

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


4

chỉ vars(obj), nó sẽ nêu toàn bộ giá trị của đối tượng

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

Bạn cũng có thể thêm cái này

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

Cập nhật

Câu trả lời tổng hợp mới hơn được đăng bởi @zags đầy đủ và thanh lịch hơn so với của tôi. Thay vào đó hãy tham khảo câu trả lời.

Nguyên

Nếu bạn sẵn sàng xác định phương thức to_dict của riêng mình như @karthiker đã đề xuất, thì điều đó chỉ giải quyết vấn đề này thành vấn đề đặt ra.

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

Chúng ta cần xóa các khóa ngoại được gắn nhãn sai khỏi otherDict .

Để làm điều này, chúng ta có thể sử dụng một vòng lặp tạo ra một từ điển mới có mọi mục ngoại trừ những từ có dấu gạch dưới trong đó. Hoặc, để tiết kiệm thời gian, chúng ta có thể chỉ cần thêm những bản gốc dict từ điển chỉ là bộ dưới mui xe.

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

Vì vậy, chúng tôi còn lại với dict sau :

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

Và bạn chỉ cần trả lại điều đó.

Mặt khác, bạn không thể sử dụng dấu gạch dưới trong tên trường có thể chỉnh sửa = sai. Mặt khác, điều này sẽ hoạt động cho bất kỳ tập hợp các trường trong đó các trường do người dùng tạo không chứa dấu gạch dưới.

Đây không phải là cách tốt nhất để làm điều này, nhưng nó có thể hoạt động như một giải pháp tạm thời cho đến khi tìm thấy một phương pháp trực tiếp hơn.

Đối với ví dụ bên dưới, dict sẽ được hình thành dựa trên model_to_dict và otherDict sẽ được hình thành bằng phương thức giá trị của bộ lọc. Tôi đã có thể tự làm điều này với các mô hình, nhưng tôi không thể khiến máy của mình chấp nhận OtherModel.

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

Điều đó sẽ đưa bạn vào một sân bóng thô sơ của câu trả lời cho câu hỏi của bạn, tôi hy vọng.


1
Không chắc chắn những gì bạn đang cố gắng sử dụng recho ở đây. Nếu nó là để lọc ra các khóa có dấu gạch dưới trong đó, thì đây không phải là mã chính xác hay hành vi đúng. re.match("_", "reference1_id")trả về Nonevà các cột hợp pháp trong cơ sở dữ liệu có thể có dấu gạch dưới trong tên của chúng.
Zags

re.match ("_", "Reference1_id") không trả về Không, đáng lẽ nó phải là: re.match (". * _. *", "Reference1_id")
Tiện ích

Tôi đã thực hiện một số thay đổi để loại bỏ ví dụ xấu và bao gồm một ví dụ tốt hơn. Tôi cũng đã thay đổi một số điều để bày tỏ rằng đây sẽ là một giải pháp tạm thời cho một tập hợp con của tất cả các mô hình. Tôi không biết bạn sẽ làm gì cho các mô hình có dấu gạch dưới trong các editable=falselĩnh vực của họ . Tôi chỉ cố gắng cung cấp một cái gì đó mà bạn có thể làm việc cho đến khi một giải pháp kinh điển hơn có thể được đưa ra.
Tiện ích

Có thể sử dụng "_" in stringchứ không phải retrong trường hợp đó.
Zags

Vâng, đó sẽ là một cách dễ dàng hơn để làm điều đó. Nó đã không xảy ra với tôi để sử dụng nó theo cách này, nhưng nó hoàn toàn có ý nghĩa tốt bây giờ. Tôi đã thay đổi câu trả lời để sử dụng inthay vì re.
Tiện ích

2

Rất nhiều giải pháp thú vị ở đây. Giải pháp của tôi là thêm một phương thức as_dict vào mô hình của tôi với sự hiểu biết chính tả.

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

Như một phần thưởng, giải pháp này kết hợp với việc hiểu danh sách qua một truy vấn tạo ra một giải pháp tốt nếu bạn muốn xuất các mô hình của mình sang một thư viện khác. Ví dụ: bỏ các mô hình của bạn vào khung dữ liệu gấu trúc:

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

1
Điều này hoạt động tốt đối với các trường giá trị như chuỗi và int, nhưng sẽ gặp một số vấn đề với khóa ngoại và thậm chí nhiều hơn với nhiều trường
Zags

Điểm rất tốt! Đặc biệt là đối với nhiều người. Người ta muốn đưa một số điều kiện vào để xử lý các trường hợp đó một cách thích hợp, hoặc giới hạn giải pháp này cho các mô hình đơn giản. Cảm ơn.
t1m0

1

(không có nghĩa là để bình luận)

Ok, nó không thực sự phụ thuộc vào các loại theo cách đó. Tôi có thể đã hiểu sai câu hỏi ban đầu ở đây vì vậy hãy tha thứ cho tôi nếu đó là trường hợp. Nếu bạn tạo serliazers.py thì ở đó bạn tạo các lớp có các lớp meta.

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

Sau đó, khi bạn nhận được dữ liệu trong lớp xem, bạn có thể:

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

Đó là khá nhiều những gì tôi có trong vô số địa điểm và nó trả về JSON đẹp thông qua JSONRenderer.

Như tôi đã nói đây là phép lịch sự của DjangoRestFramework nên rất đáng để xem xét.


1

Cách dễ dàng hơn là chỉ sử dụng pprint, đó là trong Python cơ sở

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

Điều này cho phép đầu ra mà trông giống như json.dumps(..., indent = 4)nhưng nó xử lý một cách chính xác các kiểu dữ liệu lạ mà có thể được nhúng trong trường hợp mô hình của bạn, chẳng hạn như ModelStateUUIDvv

Đã thử nghiệm trên Python 3.7


0

Có lẽ điều này giúp bạn. Có thể điều này không biến đổi nhiều thành nhiều mối quan hệ, nhưng es khá tiện dụng khi bạn muốn gửi mô hình của mình ở định dạng 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

Giải pháp tốt nhất bạn từng thấy.

Chuyển đổi thể hiện django.db.models.Model và tất cả các trường hàm ForeignKey, ManyToManyField và @Property thành 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

Câu trả lời từ @zags là toàn diện và nên đủ nhưng phương pháp số 5 (là IMO tốt nhất) đưa ra một lỗi để tôi cải thiện chức năng của trình trợ giúp.

Vì OP yêu cầu chuyển đổi many_to_manycác trường thành một danh sách các khóa chính thay vì danh sách các đối tượng, tôi đã nâng cao hàm để giá trị trả về bây giờ là tuần tự hóa JSON - bằng cách chuyển đổi datetimecác đối tượng thành strmany_to_manycác đối tượng thành một danh sách 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

Bạn nhận được lỗi gì? Tôi rất vui khi cập nhật câu trả lời
Zags

Hiện tại chức năng của các vòng lặp thông qua concrete_fieldsm2m_fieldstrông giống hệt nhau, vì vậy giả sử m2m_fieldsvòng lặp có triển khai không chính xác ở đây.
Daniel Himmelstein

@ Lỗi là lỗi AttributeError: 'list' object has no attribute 'values_list' mà tôi không thể tìm thấy lý do đằng sau nó. Tôi đang sử dụng Django 2.1.1
Armin Hemati Nik

@ daniel-himmelstein cảm ơn bạn đã chỉ ra, tôi đã sửa mã. lý do cho các vòng lặp giống hệt nhau là do thực hiện các hoạt động khác nhau trong mã cục bộ của tôi và tôi đã quên tối ưu hóa nó cho câu trả lời SO.
Armin Hemati Nik

@ArminHemati Django đã thay đổi việc thực hiện field.value_from_objectvà kết quả là model_to_dict. Tôi đã cập nhật phần 5 của câu trả lời của tôi để phản ánh điều này.
Zags
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.