1、model_to_dict源代码分析
1.1、转换时丢失日期字段的问题
Python中用 django.forms.models import model_to_dict 转换QuerySet为字典(模型类实例对象转字典)时会丢失日期数据,是因为 DateTime 字段有auto_now_add=True和auto_now=True属性的,会默认添加editable=False隐藏属性。 可通过添加日期键值数据获取。
我们可以通过model_to_dict的源代码,得知模型类实例对象的属性(即:数据库表字段)的editable属性为False时,model_to_dict方法 在将模型类实例对象(即:数据表记录)转字典时,不会返回该对象属性(即:表字段)
源代码: django.forms /models.py
def model_to_dict(instance, fields=None, exclude=None):
"""
Return a dict containing the data in ``instance`` suitable for passing as
a Form's ``initial`` keyword argument.
``fields`` is an optional list of field names. If provided, return only the
named.
``exclude`` is an optional list of field names. If provided, exclude the
named from the returned dict, even if they are listed in the ``fields``
argument.
"""
opts = instance._meta
data = {}
for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many):
if not getattr(f, 'editable', False):
# 如果这个对象没有editable属性,返回False
# 就不把这个对象(表字段)返回了
continue
if fields is not None and f.name not in fields:
continue
if exclude and f.name in exclude:
continue
data[f.name] = f.value_from_object(instance)
return data
如下图,调用接口时,QuerySet对象转字典(表记录转字典)时,没有返回日期类型的字段
1.2、没有返回外键对应表的记录
2、优化model_to_dict方法
2.1、优化后解决的问题
- 解决了模型类实例对象转换为字典时,日期类型字段丢失的问题
- 解决了外键字段对应表记录 一起返回的问题
2.2、优化后的代码
from itertools import chain
import datetime
from django.db.models.fields.related import ManyToManyField, ForeignKey, OneToOneField
from django.core.files import File
def model_to_dict(instance, fields=None, exclude=None):
if instance == None: #如果说传过来空的值,那么就直接返回
return
opts = instance._meta
data = {}
for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many):
# 其中参数instance是对象实例,fields是指定需要哪些字段,exclude是指定排除哪些字段,exclude比fields优先级高。
# if not getattr(f, 'editable', False):
# 将这里注释掉,转字典时,就会返回editable属性为False的字段(包括日期类型)
# continue
if fields is not None and f.name not in fields:
# 实现返回指定表字段
continue
if exclude and f.name in exclude:
# 实现不返回指定表字段
continue
# data[f.name] = f.value_from_object(instance)
value = f.value_from_object(instance)
# 拿到这个模型类实例对象属性(即表字段的值)
if type(value) == datetime.datetime:
# 如果这个模型类实例对象属性(即表字段)的类型是日期类型
value = value.strftime("%Y-%m-%d %H:%M:%S")
# 把这个值 日期格式化一下(这个值就是日期类型)
elif type(value) == datetime.date:
# 如果这个模型类实例对象属性(即表字段)的类型是日期类型
value = value.strftime("%Y-%m-%d")
# 把这个值 日期格式化一下(这个值就是日期类型)
elif type(f) == ForeignKey:
# 如果这个模型类实例对象属性(即表字段)的类型是一对多外键
foreign_key_obj = getattr(instance, f.name)
# 取外键的对象(即外键对应的表记录)
# instance.grade 被注释优化的代码
value = model_to_dict(foreign_key_obj)
# 把外键外键的对象(即外键对应的表记录)转为字典
elif type(f) == OneToOneField:
# 如果这个模型类实例对象属性(即表字段)的类型一对一是外键
foreign_key_obj = getattr(instance, f.name)
# 取外键的对象(即外键对应的表记录)
value = model_to_dict(foreign_key_obj)
# 把外键外键的对象(即外键对应的表记录)转为字典
elif type(f) == ManyToManyField:
# 如果这个模型类实例对象属性(即表字段)的类型是多对多外键
many_to_many_list = [model_to_dict(item) for item in value]
# 列表生成式:遍历多对多的 queryset 列表,
# 将每个实例对象(表记录)转为字典,放到列表中
value = many_to_many_list
if isinstance(value, File):
# 如果这个模型类实例对象属性(即表字段的值)包含文件类型
value = str(value)
# /xxx/xx/a.jpg # 就把文件的路径返回就可以了
data[f.name] = value
# 字典 key:模型类实例对象属性名,即表字段名字 value:模型类实例对象属性值,即表字段值
return data # 返回这个字典
2.2.1、使用优化后的model_to_dict
- 不建议在django.forms.models 这个源代码文件中修改model_to_dict方法
- 可以直接创建一个工具文件,将优化后的model_to_dict方法写成自定义函数;
- 以后再用到model_to_dict这个方法的时候,就直接引用工具里的自定义函数即可;
- 不用再引用django.forms.models 里的源代码方法了
2.2.2、验证优化
- 解决了模型类实例对象转换为字典时,日期类型字段丢失的问题
- 解决了外键字段对应表记录 一起返回的问题