本文章的代码已上传至github上(github包含了更多功能,相关文章后续更新)

AGL1994/django-building​github.com

前言

目前Django比较知名的序列化框架有DRF,还有Django自带的model_to_dict函数。DRF序列化需要
为model写一个serializers类,同时需要配置相关的序列化字段,如果有外键关联等,也需要作出相
关的配置。如果一个项目从0开始的话,还是建议使用DRF。对于Django自带的model_to_dict函数,
还是有一些缺点,不能序列化Foreign的值,或者某些时间格式会报错等。下面我们就进入正题。本例
子会使用反射相关的功能,也需要对Django框架有一个初步的了解。不清楚的童鞋可以先自行了解一下,
网上相关的帖子很多,这里就不多做讲解。

model

class User(AbstractUser):phone = models.CharField(max_length=11, blank=True, null=True, verbose_name="电话号码")name = models.CharField(max_length=45, blank=True, null=True, verbose_name="姓名")sex = models.IntegerField(blank=True, null=True, choices=((1, "男"), (2, '女')), default=1)birthday = models.DateField(blank=True, null=True, verbose_name="出生日期")avatar = models.CharField(max_length=255, default=None, blank=True, null=True, verbose_name="用户头像")status = models.SmallIntegerField(default=1, choices=USER_STATUS, verbose_name="状态")created = models.DateTimeField(verbose_name=u"创建时间", editable=False, auto_now_add=True)updated = models.DateTimeField(verbose_name=u"修改时间", editable=False, auto_now=True)deleted = models.SmallIntegerField(default=0)class Meta:db_table = 'tb_user'verbose_name = "用户信息"verbose_name_plural = verbose_nameclass UserWx(models.Model):user = models.ForeignKey(User, on_delete=models.DO_NOTHING, null=True, blank=True)openid = models.CharField(max_length=50, verbose_name='openid')nickname = models.CharField(max_length=255, verbose_name='用户昵称')sex = models.IntegerField(choices=USER_SEX, verbose_name='性别')province = models.CharField(max_length=255, verbose_name='省份')city = models.CharField(max_length=255, verbose_name='城市')country = models.CharField(max_length=255, verbose_name='国家')head_img_url = models.CharField(max_length=500, verbose_name='用户头像url')privilege = models.CharField(max_length=1000, verbose_name='用户特权信息')created = models.DateTimeField(verbose_name=u"创建时间", editable=False, auto_now_add=True)updated = models.DateTimeField(verbose_name=u"修改时间", editable=False, auto_now=True)deleted = models.SmallIntegerField(default=0)class Meta:verbose_name = '用户微信信息'db_table = 'tb_user_wx'

思路及步骤

1. 查出数据,得到需要序列化的数据,本例直接使用QueryDict对象进行序列化。
2. 得到被序列化model的每一个字段。
3. 根据每一个字段不同的类型作出不同的处理。
4. 返回结果字典。
5. 使用json模块,将字典转成json。

代码

# 获取查询结果集
user_wx = UserWx.objects.select_related('user').all()  # 获取查询的结果集
result_dict = model_serializer(user_wx)
result = json.dumps(result_dict) # 将结果集转成json即可# 调用序列化方法,注:以下可选参数为拓展功能,可先忽略,对功能实现没有多大影响
def model_serializer(cls, choices=True, contains=(), excepts=('updated', 'deleted')):"""model转json:param choices: 是否自动转换choices:param cls: 需要转的model:param contains: 包含的字段 (若有此参数,则只返回次参数包含字段):param excepts: 排除字段,默认排除更新时间与deleted  (若有此参数,则排除此参数内的字段):return:实现models转dict支持了时间格式转化, foreignKey, choice类型转义暂不支持 ManyToMany ManyToOne 等"""try:# 查询是否有外键的数据,为保证性能,只会序列化使用了select_related()的外键数据。foreign_dict = cls.query.select_relatedexcept Exception:foreign_dict = {}return _model_serializer(cls, choices, contains, excepts, foreign_dict)def _model_serializer(cls, choices, contains, excepts, foreign_dict={}):# 此方法主要用于判断传入的序列化对象是QueryDict或者是一个model。如果是QueryDict则返回list(dict),如果是model,则返回dictmodel_dict = []single = Falseif not isinstance(cls, Iterable):cls = (cls,)single = Truefor instance in cls:  # 如果是QueryDict,则遍历每一条数据model_dict.append(_model_to_dict(instance, choices, contains, excepts, foreign_dict))if single:model_dict = model_dict[0]return model_dictdef _model_to_dict(instance, choices, contains, excepts, foreign_dict):"""model 转 dict:param foreign_dict::param contains::param excepts::param instance::return: dict"""field_dict = {}foreign_list = foreign_dict.keys() if isinstance(foreign_dict, dict) else ()  # 待序列化的外键列表fields = instance._meta.fields  # 获取当前期model的所有字段for field in fields:# 获取字段名。Django有两个字段属性,name和attname。name是model里面写的属性名,attname则是 name_id(针对Foreign等)name = field.name  # 排除不需要的参数(为拓展功能内容)if contains and (name not in contains):continueif name in excepts:continuetry:# 首先判断是否为ForeignKey类型,ForeignKey需要用到name与attname,所以先单独处理if isinstance(field, models.ForeignKey):if name in foreign_list:  # 如果ForeignKey字段包含在select_related(),在需要对ForeignKey的model做序列化,否则直接返回ForeignKey的值value = getattr(instance, name)foreign_list_child = foreign_dict.get(name)# 如果有数据,则递归value = _model_serializer(value, choices=choices, contains=contains,excepts=excepts, foreign_dict=foreign_list_child)else:value = getattr(instance, field.attname)value = value if value else ''field_dict[name] = valuecontinueelse:value = getattr(instance, name)value = value if value else ''except Exception as e:field_dict[name] = ''continue# 上面处理了model.ForeignKey类型,下面处理其他类型。(类型不完全,可根据自己的需求添加)if isinstance(field, models.DateTimeField):  # datetimefield_dict[name] = value.strftime('%Y-%m-%d %H:%M:%S') if value else ''elif isinstance(field, models.DateField):  # datefield_dict[name] = value.strftime('%Y-%m-%d') if value else ''else:  # 其他# 判断是否有choices。(拓展功能,自动将有choices的值转化为相应的描述)if choices and hasattr(field, 'choices') and field.choices:f = getattr(instance, 'get_{}_display'.format(name))field_dict[name] = f()else:field_dict[name] = valuereturn field_dict

总结

以上第关键的代码做了一些解释说明,相信小伙伴们仔细思考应该能动。代码的功能实现总体来说还是很简单的,只需要得到别序列化对象的
字段,然后根据字段的类型作出相应的操作。但是上述代码还有很多待优化的地方,下面给出一些建议(这个建议不保证能实现,只是一些方向)。
1.减少循环次数。2.由于遍历的结果集大部分情况数据都是相同的,且相同的model的数据顺序也是相同的,我们没必要对每一天数据都做类型判
断,可以在第一次循环后,缓存相关数据类型与操作。3.更多的拓展功能,比如只是OneToOne,ManyToMany等。

最后

帖子有什么不当的地方,欢迎大佬指出。

django 不包括字段 序列化器_手写一个Django序列化功能相关推荐

  1. 用 Go 手写一个 JSON 序列化器

    用 Go 手写一个 JSON 序列化器 方案 实现 字符串转义 忽略类型 序列化器主体 数字和逻辑类型 字符串类型 数组类型 字典类型 自定义结构类型 指针类型 API 使用 安装 调用 测试 开源和 ...

  2. 未能加载文件或程序集或它的某一个依赖项_手写一个miniwebpack

    前言 之前好友希望能介绍一下 webapck 相关的内容,所以最近花费了两个多月的准备,终于完成了 webapck 系列,它包括一下几部分: webapck 系列一:手写一个 JavaScript 打 ...

  3. 手写一个promise用法_手写一个 Promise

    1 js 的基本数据类型? 2 JavaScript 有几种类型的值? 3 什么是堆?什么是栈?它们之间有什么区别和联系? 4 内部属性 [Class] 是什么? 5 介绍 js 有哪些内置对象? 6 ...

  4. 面试官:请手写一个带取消功能的延迟函数,axios 取消功能的原理是什么

    大家好,我是若川.最近组织了源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步.同时极力推荐订阅我写的<学习源码整体架构系列> ...

  5. glide默认的缓存图片路径地址_手写一个静态资源中间件,加深了解服务器对文件请求的缓存策略...

    上一篇文章<详解页面静态资源的缓存策略,搞懂强缓存和协商缓存再做性能优化>我们从理论上介绍了浏览器和服务器是如何对静态资源做缓存的,这篇文章我们把它做成一个node服务器的静态资源中间件. ...

  6. html如何获取请求头变量的值。_手写一个静态资源中间件,加深了解服务器对文件请求的缓存策略...

    上一篇文章<详解页面静态资源的缓存策略,搞懂强缓存和协商缓存再做性能优化>我们从理论上介绍了浏览器和服务器是如何对静态资源做缓存的,这篇文章我们把它做成一个node服务器的静态资源中间件. ...

  7. 手写一个promise用法_手写一个Promise

    JS面向对象 在JS中一切皆对象,但JS并不是一种真正的面向对象(OOP)的语言,因为它缺少类(class)的概念.虽然ES6引入了class和extends,使我们能够轻易地实现类和继承.但JS并不 ...

  8. 手写一个promise用法_手写一个自己的 JavaScript Promise 类库

    终于有时间写这篇文章了, ES2015 推出了JS 的 Promise ,而在没有原生支持的时候,我们也可以使用诸如 Promises/A+ 的库的帮助,在我们的代码里实现Promise 的支持: 如 ...

  9. 如何手写一个支持H.265的高清播放器

    概述 音视频编解码技术在当前的互联网行业中十分热门,特别是高清视频播放器的开发,其中包括4K.8K等超高清分辨率的播放器,具有极高的市场需求和广泛的应用场景.H265编码技术更是实现高清视频压缩的重要 ...

最新文章

  1. Lync 2010迁移Lync 2013 PART6:迁移CMS
  2. WordPress更新时提示无法连接到FTP服务器的解决方案
  3. 【风险管理】风控决策系统
  4. Parse Too complex in xxxx.cpp --------source insight
  5. 4.3.7 基于类的访问权限
  6. Java笔记(一)向上向下转型,枚举,初始化,可变长参数,代理,多态,继承
  7. 前端性能优化之重排和重绘
  8. SpringBoot使用thymefeal出现No mapping for GET /xxx的解决办法
  9. 在Mac中关闭应用通知的两种方法
  10. 独奏骑士服务器维护,独奏骑士新手天赋怎么选?新手天赋选择攻略
  11. VSCode创建vue模板(快捷方便)
  12. Maya2018安装报错(错误代码1612)
  13. php微信退款结果通知,微信支付退款结果通知解密
  14. 【第六届强网杯CTF-Wp】
  15. 【LeetCode LCP 3】机器人大冒险
  16. 增长工程日 | 从战略到战术,如何搭建新消费品牌增长体系
  17. Amazon亚马逊卖家设置World First(WF卡)收款教程!
  18. Oracle RAC集群三种心跳机制
  19. 字符串转base64,base64转字符串
  20. oracle11g数据库导入导出

热门文章

  1. python功能代码_整理几个常用的Python功能代码片段【收藏】
  2. raspberrypi python传感器_Raspberry Pi和Arduino读取串行传感器d
  3. 深入php内核,从底层c语言剖析php实现原理
  4. 使用SpringBoot Admin监控SpringCloud微服务
  5. presto spill to disk
  6. 【转】idea激活搭建授权服务器
  7. 前端常用linux命令
  8. [C++] 用Xcode来写C++程序[3] Constants
  9. 【JOURNAL】集花酒联
  10. Silverlight 应用 WCF RIA Services 在 IIS6 部署问题总结