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 asa Form's ``initial`` keyword argument.``fields`` is an optional list of field names. If provided, return only thenamed.``exclude`` is an optional list of field names. If provided, exclude thenamed from the returned dict, even if they are listed in the ``fields``argument."""opts = instance._metadata = {}for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many):if not getattr(f, 'editable', False):# 如果这个对象没有editable属性,返回False# 就不把这个对象(表字段)返回了continueif fields is not None and f.name not in fields:continueif exclude and f.name in exclude:continuedata[f.name] = f.value_from_object(instance)return data
如下图,调用接口时,QuerySet对象转字典(表记录转字典)时,没有返回日期类型的字段
1.2、没有返回外键对应表的记录
2、优化model_to_dict方法
2.1、优化后解决的问题
- 解决了模型类实例对象转换为字典时,日期类型字段丢失的问题
- 解决了外键字段对应表记录 一起返回的问题
2.2、优化后的代码
from itertools import chainimport datetimefrom django.db.models.fields.related import ManyToManyField, ForeignKey, OneToOneFieldfrom django.core.files import Filedef model_to_dict(instance, fields=None, exclude=None):if instance == None: #如果说传过来空的值,那么就直接返回returnopts = instance._metadata = {}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的字段(包括日期类型)# continueif fields is not None and f.name not in fields:# 实现返回指定表字段continueif 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_listif 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、验证优化
- 解决了模型类实例对象转换为字典时,日期类型字段丢失的问题
- 解决了外键字段对应表记录 一起返回的问题

