前言

好久没更新博客了,最近依然是在做之前博客说的这个项目:项目完成 - 基于Django3.x版本 - 开发部署小结

这项目因为前期工作出了问题,需求没确定好,导致了现在要做很多麻烦的工作,搞得大家都身心疲惫。唉,只能说技术团队,有里一个靠谱有能力的领导是非常重要的。

进入正题

本文继续记录Django项目开发的一些经验。

本次的项目依然基于我定制的「DjangoStarter」项目模板来开发,该项目模板(脚手架)整合了一些常用的第三方库以及配置,内置代码生成器,只要专注业务逻辑实现即可。

数据批量导入

上篇文章说到我写了脚本导入大量数据的时候很慢,然后有网友评论可以使用bulk_create,所以在第二期的新增需求中,我处理完数据就使用bulk_create来导入,速度确实有了可观的提升,应该是能达到原生SQL的性能。

先把Model的实例全都添加到列表里面,然后再批量导入,就很快了。

写了个伪代码例子

result = data_proc()
data = [] for item in result:
print(f"处理:{item['name']}")
data.append(ModelObj(name=item['name'])) print('正在批量导入')
ModelObj.objects.bulk_create(data)
print('完成')

还有除了这个批量新增的API,DjangoORM还支持批量更新,bulk_update,用法同这个批量新增。

数据处理

上次需求很急的情况下,拿到了几百M的Excel数据之后,我直接用Python的openpyxl库来预处理成JSON格式,然后再一条条导入数据库

而且这些数据还涉及到多个表,这就导致了数据处理和导入速度异常缓慢

当时是DB manager直接把Excel导入到数据库临时表处理的,但是后面发现SQL处理的数据,清洗过后还是出了很多错误

所以后面来的新一批数据,我选择自己来搞,SQL还是不太适合做这些数据清洗~

直接用openpyxl来处理Excel也太外行了,Python做数据分析有很多工具,都可以利用起来,比如pandas

这次新需求给的Excel很恶心,里面一堆合并的单元格,虽然是好看,但要导入数据库很麻烦啊!

不过还好pandas的数据处理功能足够强大,可以应付这种情况,然后为了整个数据处理的过程更直观,我安排上了jupyter,pycharm现在已经集成了,体验比网页版的好一点,不过实际使用的时候发现有一些bug,有些影响体验。

数据例子如下

第一列序号是没用的,不管,然后看到这里面姓名的人和家庭成员直接应该是一对多的关系,为了好看合并了单元格,这样处理的时候就很恶心了,合并后的单元格,pandas读取进来只有第一行是有数据的。

不过我们可以用pandas的数据补全功能来处理。

简单的处理单元格合并的代码参考:

import pandas as pd

xlsx1 = pd.ExcelFile('文件名.xlsx')
# 参数0代表第一个工作表,header=0代表第一行作为表头,uescols表示读取的列范围,我们不要第一列那个序号
df = pd.read_excel(xlsx1, 0, header=0, usecols='B:G') df['姓名'].fillna(method='pad', inplace=True)
df['性别'].fillna(method='pad', inplace=True)
df['出生年月'].fillna(method='pad', inplace=True)
df['联系人'].fillna(method='pad', inplace=True)
df['联系电话'].fillna(method='pad', inplace=True)

代码里有注释,用fillna填充缺失的字段即可

然后再把DateFrame转换成比较容易处理的JSON格式(其实在Python里是dict)

json_str = df.to_json(orient='records')
parsed = json.loads(json_str)

这样出来就是键值对的数据了

PS:好像可以直接遍历df来获取数据,转JSON好像绕了一圈,不过当时比较急没有研究

参考资料

admin后台优化

定制化的项目其实Django Admin后台用得也不多了,不过作为报表看看数据或者进行简单的筛选操作还是足够的。

本项目的admin界面基于simpleUI库定制

从上一篇文章可以看到我对admin后台的主页进行了重写替换,效果如下

这个界面是用Bootstrap和AdminLTE实现的,AdminLTE这个组件库确实不错,在Bootstrap的基础上增加了几个很好看的组件,很有用~

然后图标用的font-awesome,图表用的是chart.js,都属于是看看文档就会用的组件,官网文档地址我都整理在下面了,自取~

有一点要注意的是,在SimpleUI里,自定义的主页是以iframe的形式实现的!而SimpleUI本身是Vue+ElementUI,所以想要在主页里跳转到admin本身的其他页面是很难实现的!这点要了解,我暂时没想到什么好的办法,要不下次试试别的admin主题好了~

参考资料

继续说Django的聚合查询

上一篇文章有提到聚合查询,但是没有细说,本文主要介绍这几个:

  • aggregate
  • annotate
  • values
  • values_list

根据我目前的理解,aggregateannotate的第一个区别是,前者返回dict,后者返回queryset,可以继续执行其他查询操作。

aggregate

然后就是使用场景的区别,aggregate一般用于整体数据的统计,比如说

统计用户的男女数量

from django.db.models import Count

result1 = User.objects.filter(gender='男').aggregate(male_count=Count('pk', distinct=True))
result2 = User.objects.filter(gender='女').aggregate(female_count=Count('pk', distinct=True))

PS:其实这里的Count函数里,可以不加distinct参数的,毕竟主键(pk)应该是不会重复的

这样返回的数据是

# result1
{
"male_count": 100
} # result2
{
"female_count": 100
}

应该很容易理解

annotate

annotate的话,一般是搭配values这种分组操作使用,例子:

from django.db.models import Count

result1 = User.objects.values('gender').annotate(count=Count('pk'))

返回结果

[
{
"gender": "男",
"count": 100
},
{
"gender": "女",
"count": 100
}
]

简而言之,就是在values分组之后,annotate对数据进行聚合运算之后把自定义的字段插入每一组内~ 有点拗口,反正看上面的代码就好理解了。

values / values_list

最后是valuesvalues_list,作用差不多,都是提取数据表里某一列的信息,(这俩都跟分组有关)

比如说我们的用户表长这样

id name gender country
1 人1 中国
2 人2 越南
3 人3 新加坡
4 人4 马来西亚
5 人5 中国
6 人6 中国

我们可以用这段代码提取所有国家

User.objects.values("country")
# 或者
User.objects.values_list("country")

前者根据指定的字段分组后返回包含字典的Queryset

<QuerySet [{'country': '中国'}, {'country': '越南'}, {'country': '新加坡'}, {'country': '马来西亚'}, {'country': '中国'}, {'country': '中国'}]>

后者返回的是包含元组的Queryset

<QuerySet [('中国',), ('越南',), ('新加坡',), ('马来西亚',), ('中国',), ('中国',)]>

然后values_list还能加一个flat=True参数,直接返回包含数组的Queryset

<QuerySet ['中国', '越南', '新加坡', '马来西亚', '中国', '中国']>

这就可以很直观的看出来这俩函数的作用了。

然后结合上面的annotate再说一下,假如我们要计算每个国家有多少人,可以用这个代码

User.objects.values("country").annotate(people_count=Count('pk'))

结果大概是这样

[
{
"country": "中国",
"people_count": 3
},
{
"country": "越南",
"people_count": 1
},
{
"country": "新加坡",
"people_count": 3
},
{
"country": "马来西亚",
"people_count": 3
}
]

搞定~

聚合查询这方面还有很多场景例子,本文只说了个大概,后续有时间再写篇新博客来细说一下~

参考资料

使用docker部署MySQL数据库

虽然之前看到有人说MySQL不适合用docker来部署,不过docker实在方便,优点掩盖了缺点,所以本项目还是继续使用docker。

继续用docker-compose来编排容器。

首先如果在本地启动一个测试用的MySQL,可以找个空目录,单独创建一个docker-compose.yml文件,配置内容在下面,然后运行docker-compose up

下面的配置里我做了volumes映射,MySQL数据库的文件会保存在本地这个目录下的mysql-data文件夹里

version: "3"
services:
mysql:
image: daocloud.io/mysql
restart: always
volumes:
- ./mysql-data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=mysql-admin
- MYSQL_USER=test
- MYSQL_PASS=yourpassword
ports:
- "3306:3306"

使用ports开启端口,方便我们使用Navicat等工具连接数据库操作。

下面是整合在web项目中的配置(简化的配置,详细配置可以看我的DjangoStarter项目模板)

version: "3"
services:
mysql:
image: daocloud.io/mysql
restart: always
volumes:
- ./mysql-data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=mysql-admin
# 注意这里使用expose而不是ports里,这是暴露端口给其他容器使用,但docker外部就无法访问了
expose:
- 3306
web:
restart: always # 除正常工作外,容器会在任何时候重启,比如遭遇 bug、进程崩溃、docker 重启等情况。
build: .
command: uwsgi uwsgi.ini
volumes:
- .:/code
ports:
- "80:8000"
# 在依赖这里指定mysql容器,然后才能连接到数据库
depends_on:
- mysql

关键的配置我写了注释,很好懂。

参考资料

关于缓存问题

上一篇文章有提到缓存的用法,Redis搭配Django原生的缓存装饰器cache_page是没啥问题的,但用第三方的drf-extensions里的cache_response装饰器的时候,就有个问题,不能针对不同的query params请求参数缓存响应

比如说下面这两个地址,虽然是指向同一个接口,但参数不同,按理说应该返回不同的数据。

但加上cache_response装饰器之后,无论传什么参数都返回同一个结果,目前我还没搞清楚是我哪里写错了还是这个库的bug~

性能优化

老生常谈…

上篇文章也说了一点,不过没有具体。都说DjangoORM性能差,其实瓶颈还是在数据库IO这块,在耗时最长的IO操作面前,那点性能劣势其实也不算什么了(特别是我们这种toB的系统,没有高并发的需求)

经过profile性能分析,瓶颈基本都在哪些统计类的接口,这类接口的特征就是要关联多个表查询,经常一个接口内需要多次请求数据库,所以优化思路就很明确了,减少数据库请求次数。

两种思路

  • 一种是一次性把数据全部取出到内存,然后用pandas这类数据分析库来做聚合处理;
  • 一种是做先做预计算,然后保存中间结果,下次请求接口的时候直接去读取中间结果,把中间结果拿来做聚合

最终我选择使用第二种方式,并且选择把中间结果存在MongoDB数据库里

小结

本项目到这里只是出了一个阶段性的成果,还是未完结,从这个项目中也发现了很多问题,团队的、自身的,都有。

团队的话,我们这的领导属于是不太了解技术那种,然后抗压能力比较差,平时任务不紧急的时候就不怎么干扰我们的进度,在项目比较急的情况下就乱套了,瞎指挥、乱提需求、乱干扰进度,总之就是添乱拖后腿…

当然最大的问题还是出在政企部门,前期在和客户的沟通中出了很大的问题,当然这可能和国企的架构混乱也有关系,甚至在协议方面也出了大问题,根本没有把需求写清楚,导致了交付后客户无限制地增加需求。

实际上一个政企项目涉及到太多非技术因素了,其实这本不是咱技术人员需要关心的,但现实就是这样,唉。

项目完成小结 - Django3.x版本 - 开发部署小结 (2)的更多相关文章

  1. 项目完成 - 基于Django3.x版本 - 开发部署小结

    前言 最近因为政企部门的工作失误,导致我们的项目差点挂掉,客户意见很大,然后我们只能被动进入007加班状态,忙得嗷嗷叫,直到今天才勉强把项目改完交付,是时候写一个小结. 技术 因为前期需求不明确,数据 ...

  2. IbatisNet开发使用小结

    一.   介绍 平常做企业级应用,需求变化是经常的事,而很多基础代码重复也是很让人头疼的问题.所以很多人会使用一些ORM框架来增强项目的可维护性.可扩展性.IBatis.Net就是一个比较易用的ORM ...

  3. 前端开发个人小结 · Retrospection的博客

    序 2018年转眼来到了最后一个月,算下来我进入前端之门也有一年了,虽然下半年由于忙于筹备毕业论文的相关事项,前端这一块有所放下,但是想想还是给自己这一年的学习做一个总结. 现代化软件开发确实是一个复 ...

  4. iPhone图形开发绘图小结

    iPhone图形开发绘图教程是本文要介绍的内容,介绍了很多关于绘图类的使用,先来看详细内容讲解. 1.绘图总结: 绘图前设置: CGContextSetRGBFillColor/CGContextSe ...

  5. NutzWk 5.0.x 微服务分布式版本开发及部署说明

    NutzWk 5.x 已发布一段时间,这段时间基于此版本开发了智慧水务系统(NB-IOT).某物联网平台.某设备租赁平台.某智慧睡眠平台.某智慧园区项目等,开发和部署过程中遇到一些小问题,开这个帖子把 ...

  6. andriod socket开发问题小结

    andriod socket开发问题小结 个人信息:就读于燕大本科软件project专业 眼下大四; 本人博客:google搜索"cqs_2012"就可以; 个人爱好:酷爱数据结构 ...

  7. Alpha版本第二周小结

               软工作业---Alpha版本第二周小结   姓名 学号 周前计划 每周实际工作记录 自我打分 yrz 1417 协助原型设计的完善,督促组员完成个人任务 原型优化设计未完成,但体 ...

  8. 软工作业-----Alpha版本第一周小结

            软工作业-----Alpha版本第一周小结   Part1.第一周周计划记录 姓名 学号 周前计划安排 每周工作记录 自我打分 yrz(队长) 1417 1.进行任务分析 2.任务分配 ...

  9. Zabbix 5.0 LTS版本的安装小结

    Zabbix 5.0 LTS版本的安装小结   1:准备Zabbix的服务器. 这里可能需要一台或多台服务器,视需求和资源而定.也可以将Zabbix_Server.MySQL.Zabbix Web等安 ...

随机推荐

  1. jmeter的安装使用

    以前没自己做过压力测试,一直都是测试在做.现在需要自己做压力测试了,特别学习下jmeter的使用方法.现在做下记录: 1.下载jmeter,这个忽略,百度到处都是 2.打开jmeter,jmeter的 ...

  2. github 编写README时常用的写法

    参考:https://github.com/HeTingwei/ReadmeLearn#%E7%BC%96%E5%86%99readme%E6%97%B6%E5%B8%B8%E7%94%A8%E7%9 ...

  3. 快速注册service服务

    一.通过SpringContextHolder的getBean来注册service服务 导入的包:import com.bessky.platform.context.SpringContextHol ...

  4. 记录md的偏好设置

  5. 妙趣横生的HTML5 Page Visibility API

    起因 最近浏览36kr 的网页的时候偶然发现一个有趣的情况:当浏览器Tab 处于非当前页的时候,36kr 的标题栏会自动换成可爱的大白字符形象.Jeff 在第一次看到的时候就知道这种效果是通过HTML ...

  6. Episode 1:正视微信(试播)

    本期是 WEB VIEW 的第一期播客节目. 「不囿于 WEB,不止于 VIEW」,WEB VIEW 是由 yin 和敬礼主持的一档泛科技播客.节目中我们谨慎考量技术进步所带来的优缺点,提倡用「人治」 ...

  7. ios audio不能够正常播放

    ios中audio不能直接通过audio.play()播放,需要用户在click事件或者touch事件中执行audio.play()才能播放. ajax回调中audio.play()音乐不能正常播放. ...

  8. Muse UI遇到的坑

    写在前面:我只是一个前端小白,文章中的提到可能会有不足之处,仅提供一个参考.若有不完善的地方,欢迎各位大佬指出,希望对你有帮助! 故事背景是这样的,最近做一个Vue项目,使用到 Muse UI 组件库 ...

  9. smdms超市订单管理系统之登录功能

    一.超市订单管理系统准备阶段 Supermarket order management system 创建数据库 数据库代码放置如下 点击查看数据库address代码 CREATE TABLE `sm ...

  10. 使用pyinstaller库打包文件

    1.pyinstaller的安装 先win+r打开cmd,安装具体命令如下: pip3 install pyinstaller  2.使用pyinstaller库打包文件 假设Python源文件LPR ...