前言
项目完成 - 基于Django3.x版本 - 开发部署小结
进入正题
本文继续记录Django项目开发的一些经验。
DjangoStarter项目模板来开发,该项目模板(脚手架)整合了一些常用的第三方库以及配置,内置代码生成器,只要专注业务逻辑实现即可。
数据批量导入
先把的实例全都添加到列表里面,然后再批量导入,就很快了。
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还支持批量更新,,用法同这个批量新增。
数据处理
而且这些数据还涉及到多个表,这就导致了数据处理和导入速度异常缓慢
所以后面来的新一批数据,我选择自己来搞,SQL还是不太适合做这些数据清洗~
这次新需求给的Excel很恶心,里面一堆合并的单元格,虽然是好看,但要导入数据库很麻烦啊!
数据例子如下
不过我们可以用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)
代码里有注释,用填充缺失的字段即可
json_str = df.to_json(orient='records')
parsed = json.loads(json_str)
这样出来就是键值对的数据了
参考资料
http://zhangqijun.com/2733-2/
使用Pandas读取结构不良 Excel 的2个方法:https://www.shouxicto.com/article/1642.html
admin后台优化
本项目的admin界面基于simpleUI库定制
然后图标用的font-awesome,图表用的是chart.js,都属于是看看文档就会用的组件,官网文档地址我都整理在下面了,自取~
在SimpleUI里,自定义的主页是以iframe的形式实现的!而SimpleUI本身是Vue+ElementUI,所以想要在主页里跳转到admin本身的其他页面是很难实现的!这点要了解,我暂时没想到什么好的办法,要不下次试试别的admin主题好了~
参考资料
AdminLTE:
https://adminlte.io/docs/3.2/components/boxes.htmlFontAwesome:http://www.fontawesome.com.cn/faicons/
Chart.js:https://www.chartjs.org/docs/latest/
Django实战: 手把手教你配置Django SimpleUI打造美丽后台(多图):https://zhuanlan.zhihu.com/p/372185998
继续说Django的聚合查询
aggregate
annotate
values
values_list
根据我目前的理解,和的第一个区别是,前者返回dict,后者返回queryset,可以继续执行其他查询操作。
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:其实这里的函数里,可以不加参数的,毕竟主键()应该是不会重复的
# result1
{
"male_count": 100
}
# result2
{
"female_count": 100
}
应该很容易理解
annotate
from django.db.models import Count
result1 = User.objects.values('gender').annotate(count=Count('pk'))
返回结果
[
{
"gender": "男",
"count": 100
},
{
"gender": "女",
"count": 100
}
]
简而言之,就是在分组之后,对数据进行聚合运算之后把自定义的字段插入每一组内~ 有点拗口,反正看上面的代码就好理解了。
values / values_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 [{'country': '中国'}, {'country': '越南'}, {'country': '新加坡'}, {'country': '马来西亚'}, {'country': '中国'}, {'country': '中国'}]>
后者返回的是包含元组的
<QuerySet [('中国',), ('越南',), ('新加坡',), ('马来西亚',), ('中国',), ('中国',)]>
然后还能加一个参数,直接返回包含数组的
<QuerySet ['中国', '越南', '新加坡', '马来西亚', '中国', '中国']>
这就可以很直观的看出来这俩函数的作用了。
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
}
]
搞定~
参考资料
Python 教程之如何在 Django 中实现分组查询:
https://chinese.freecodecamp.org/news/introduction-to-django-group/aggregate和annotate的区别:https://www.cnblogs.com/Young-shi/p/15174328.html
values / values_list:https://www.jianshu.com/p/e92ab45075d5
django_filter的values / values_list:https://blog.csdn.net/weixin_40475396/article/details/79529256
使用docker部署MySQL数据库
虽然之前看到有人说MySQL不适合用docker来部署,不过docker实在方便,优点掩盖了缺点,所以本项目还是继续使用docker。
首先如果在本地启动一个测试用的MySQL,可以找个空目录,单独创建一个文件,配置内容在下面,然后运行。
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"
使用开启端口,方便我们使用Navicat等工具连接数据库操作。
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
关键的配置我写了注释,很好懂。
参考资料
Docker-compose封装mysql并初始化数据以及redis:
https://www.cnblogs.com/xiao987334176/p/12669080.html你必须知道的Docker数据卷(Volume):https://www.cnblogs.com/edisonchou/p/docker_volumes_introduction.html
关于缓存问题
比如说下面这两个地址,虽然是指向同一个接口,但参数不同,按理说应该返回不同的数据。
性能优化
老生常谈…
经过profile性能分析,瓶颈基本都在哪些统计类的接口,这类接口的特征就是要关联多个表查询,经常一个接口内需要多次请求数据库,所以优化思路就很明确了,减少数据库请求次数。
一种是一次性把数据全部取出到内存,然后用pandas这类数据分析库来做聚合处理;
一种是做先做预计算,然后保存中间结果,下次请求接口的时候直接去读取中间结果,把中间结果拿来做聚合
最终我选择使用第二种方式,并且选择把中间结果存在MongoDB数据库里
小结
团队的话,我们这的领导属于是不太了解技术那种,然后抗压能力比较差,平时任务不紧急的时候就不怎么干扰我们的进度,在项目比较急的情况下就乱套了,瞎指挥、乱提需求、乱干扰进度,总之就是添乱拖后腿…
实际上一个政企项目涉及到太多非技术因素了,其实这本不是咱技术人员需要关心的,但现实就是这样,唉。