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

  1. def model_to_dict(instance, fields=None, exclude=None):
  2. """
  3. Return a dict containing the data in ``instance`` suitable for passing as
  4. a Form's ``initial`` keyword argument.
  5. ``fields`` is an optional list of field names. If provided, return only the
  6. named.
  7. ``exclude`` is an optional list of field names. If provided, exclude the
  8. named from the returned dict, even if they are listed in the ``fields``
  9. argument.
  10. """
  11. opts = instance._meta
  12. data = {}
  13. for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many):
  14. if not getattr(f, 'editable', False):
  15. # 如果这个对象没有editable属性,返回False
  16. # 就不把这个对象(表字段)返回了
  17. continue
  18. if fields is not None and f.name not in fields:
  19. continue
  20. if exclude and f.name in exclude:
  21. continue
  22. data[f.name] = f.value_from_object(instance)
  23. return data

如下图,调用接口时,QuerySet对象转字典(表记录转字典)时,没有返回日期类型的字段
image.png

1.2、没有返回外键对应表的记录

image.png

2、优化model_to_dict方法

2.1、优化后解决的问题

  1. 解决了模型类实例对象转换为字典时,日期类型字段丢失的问题
  2. 解决了外键字段对应表记录 一起返回的问题

2.2、优化后的代码

  1. from itertools import chain
  2. import datetime
  3. from django.db.models.fields.related import ManyToManyField, ForeignKey, OneToOneField
  4. from django.core.files import File
  5. def model_to_dict(instance, fields=None, exclude=None):
  6. if instance == None: #如果说传过来空的值,那么就直接返回
  7. return
  8. opts = instance._meta
  9. data = {}
  10. for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many):
  11. # 其中参数instance是对象实例,fields是指定需要哪些字段,exclude是指定排除哪些字段,exclude比fields优先级高。
  12. # if not getattr(f, 'editable', False):
  13. # 将这里注释掉,转字典时,就会返回editable属性为False的字段(包括日期类型)
  14. # continue
  15. if fields is not None and f.name not in fields:
  16. # 实现返回指定表字段
  17. continue
  18. if exclude and f.name in exclude:
  19. # 实现不返回指定表字段
  20. continue
  21. # data[f.name] = f.value_from_object(instance)
  22. value = f.value_from_object(instance)
  23. # 拿到这个模型类实例对象属性(即表字段的值)
  24. if type(value) == datetime.datetime:
  25. # 如果这个模型类实例对象属性(即表字段)的类型是日期类型
  26. value = value.strftime("%Y-%m-%d %H:%M:%S")
  27. # 把这个值 日期格式化一下(这个值就是日期类型)
  28. elif type(value) == datetime.date:
  29. # 如果这个模型类实例对象属性(即表字段)的类型是日期类型
  30. value = value.strftime("%Y-%m-%d")
  31. # 把这个值 日期格式化一下(这个值就是日期类型)
  32. elif type(f) == ForeignKey:
  33. # 如果这个模型类实例对象属性(即表字段)的类型是一对多外键
  34. foreign_key_obj = getattr(instance, f.name)
  35. # 取外键的对象(即外键对应的表记录)
  36. # instance.grade 被注释优化的代码
  37. value = model_to_dict(foreign_key_obj)
  38. # 把外键外键的对象(即外键对应的表记录)转为字典
  39. elif type(f) == OneToOneField:
  40. # 如果这个模型类实例对象属性(即表字段)的类型一对一是外键
  41. foreign_key_obj = getattr(instance, f.name)
  42. # 取外键的对象(即外键对应的表记录)
  43. value = model_to_dict(foreign_key_obj)
  44. # 把外键外键的对象(即外键对应的表记录)转为字典
  45. elif type(f) == ManyToManyField:
  46. # 如果这个模型类实例对象属性(即表字段)的类型是多对多外键
  47. many_to_many_list = [model_to_dict(item) for item in value]
  48. # 列表生成式:遍历多对多的 queryset 列表,
  49. # 将每个实例对象(表记录)转为字典,放到列表中
  50. value = many_to_many_list
  51. if isinstance(value, File):
  52. # 如果这个模型类实例对象属性(即表字段的值)包含文件类型
  53. value = str(value)
  54. # /xxx/xx/a.jpg # 就把文件的路径返回就可以了
  55. data[f.name] = value
  56. # 字典 key:模型类实例对象属性名,即表字段名字 value:模型类实例对象属性值,即表字段值
  57. return data # 返回这个字典

2.2.1、使用优化后的model_to_dict

  • 不建议在django.forms.models 这个源代码文件中修改model_to_dict方法
  • 可以直接创建一个工具文件,将优化后的model_to_dict方法写成自定义函数
  • 以后再用到model_to_dict这个方法的时候,就直接引用工具里的自定义函数即可;
  • 不用再引用django.forms.models 里的源代码方法了

image.png

2.2.2、验证优化

  1. 解决了模型类实例对象转换为字典时,日期类型字段丢失的问题
  2. 解决了外键字段对应表记录 一起返回的问题

image.png