读取excel的内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25wb = load_workbook('/Users/aka/Downloads/w_pyxl.xlsx')
ws = wb['Sheet1']
for row in ws.rows: # 遍历所有行
# 按索引取出每行的指定位置的值
print(row[0].value, row[1].value, row[2].value, row[3].value)
# 每行组合成字典返回
def parse_ws(sheet):
keys = []
for index, row in enumerate(sheet.rows):
if index == 0:
for item in row:
keys.append(item.value)
continue
values = [x.value for x in row]
yield dict(zip(keys, values))
wb = load_workbook('/Users/aka/Downloads/w_pyxl.xlsx')
ws = wb['Sheet1']
for line in parse_ws(ws):
print('姓名:', line['姓名']) # 通过表头来取值生成excel文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16from openpyxl import Workbook
wb = Workbook()
ws = wb.active
data = [
('张三', '一三班', '100'),
('李四', '一三班', '65'),
('王武', '一三班', '95')
]
ws.append(('姓名', '班级', '分数'))
for item in data:
ws.append(item)
wb.save('/Users/aka/Downloads/test.xlsx')
使用buildx创建多架构镜像
参考文档1:https://docs.docker.com/buildx/working-with-buildx/#build-multi-platform-images
需求
- 内核版本 >= 4.8
- 通过 https://docs.docker.com/engine/install/ 安装docker,或参考 https://docs.docker.com/build/buildx/install/ 安装 buildx 插件
步骤
1. 启用 binfmt_misc
1 | docker run --privileged --rm tonistiigi/binfmt --install all |
2. 创建并切换构建器
1 | docker buildx create --name mybuilder |
3. 构建镜像
1 | docker buildx build --platform linux/amd64,linux/arm64 -t openspug/spug-service --push . |
flex布局元素被挤压
问题
先看个常见的需求,元素A 设置了固定宽度 100px
,但当元素B内容过多时会挤压A的宽度。
1 | <style> |
可以看到总宽度 200px
元素A 设置的宽度 100px
应该占据一半的空间,但展示的效果明显被挤压了。
解决方法
处理办法也很简单就是 flex-shrink
属性,该属性定义了当父元素主轴空间不足时子元素的缩小比例。具体怎么缩小还受其他属性的影响,我们这里就不展开详述了,因为大部分情况下也不会遇到那么复杂场景。
针对上述例子只需要在元素A上添加 flex-shrink: 0
即可解决。
1 | .a { |
添加后的效果,完美解决 😄
Django ORM小记
对一些常用的小技巧记录。
select_for_update
select_for_update
可以做小并发的控制,其只能在事务中使用。符合条件的查询结果会被加锁,其他查询包含加锁的数据时会阻塞,直到事务完成,如果其他查询结果不包含加锁的记录则不受影响。
1 | from django.db import transaction |
select_related
select_related
在查询时通过外键关系将关联的对象一起查询出来,从而避免查询查询以优化查询性能,适合一对一/一对多关系或关联表数据量比较小的情况。
1 | Person.objects.select_related('cls') |
对应执行的sql语句
1 | SELECT `persons`.`id`, `persons`.`name`, `classes`.`id`, `classes`.`name` ... FROM `persons` LEFT OUTER JOIN `classes` ON (`persons`.`cls_id` = `classes`.`id`) |
prefetch_related
prefetch_related
一样可以用于优化查询性能,但实现方式与select_related
不同,其会产生两条sql语句,查询结果会被缓存到内存中,然后进行合并,适合对多关系或关联表数据量比较大的情况。
1 | Person.objects.prefetch_related('cls') |
对应执行的两条sql语句
1 | SELECT `persons`.`id`, `persons`.`name` ... FROM `persons`; |
2分钟搞定React服务端渲染
什么是服务端渲染这里就再解释了,网上已经有很多详细的介绍了。
为什么需要服务端渲染
这里再说下为什么需要服务端渲染,最主要解决的问题就是解决 SEO 问题了,因为 React
Vue
基于这些框架写出来的项目数据都是浏览器端动态调用后端接口获取的,包括页面的元素结构什么的都是放在 Js 文件里的。当爬虫来访问时只拿到了 <div id="app"></div>
这样一个空的 div
里边什么内容也没,十分不利于 SEO 。
有了上述的 SEO 问题,那么解决问题思路就很简单了,就是如何能让搜索引擎的爬虫爬到页面是包含了完整内容的。
如何实现
实现的方法也很多,大都是要启动个单独的服务来处理这些爬虫的请求。这里使用的是一个开源的解决方案 prerender 使用非常简单,需要有个 nodejs
运行环境,以下上官方的使用文档。
安装 pretender
1
npm install prerender
创建文件 server.js
1
2
3const prerender = require('prerender');
const server = prerender();
server.start();启动并测试
1
2
3node server.js
curl http://localhost:3000/render?url=https://www.example.com/
其原理就是启动一个 headless
的 chrome
浏览器,在渲染完成后把结果在返回给客户端。
如何部署
到服务器上部署的时候会有个麻烦的问题,服务器一般都是 Liunx
系统安装 chrome
比较麻烦,那么就可以通过 Docker
来完美解决这个问题了,我已经构建了这样一个镜像,如果要使用可以通过以下步骤使用。
获取镜像
1
docker pull registry.aliyuncs.com/leiem/prerender
启动容器
1
docker run -d --restart=always -p 3000:3000 registry.aliyuncs.com/leiem/prerender
配置Nginx规则
1
2
3
4
5
6
7
8location / {
try_files $uri /index.html;
if ($http_user_agent ~* "googlebot|bingbot|yandex|baiduspider") {
rewrite .* /render?url=$scheme://$host$request_uri break;
proxy_pass http://127.0.0.1:3000;
}
}上述规则会匹配常见的搜索引擎的 User-Agent,让这些请求通过我们搭建的服务去处理。
自己构建镜像
可以直接使用我制作好的镜像 registry.aliyuncs.com/leiem/prerender
,如果要自己构建镜像可参考以下文档。
Dockerfile
1
2
3
4
5
6
7
8FROM browserless/chrome
USER root
WORKDIR /usr/src/prerender
COPY server.js package.json ./
RUN npm install
USER blessuser
EXPOSE 3000
CMD [ "npm", "run", "start" ]server.js
1
2
3
4
5
6
7
8
9
10
11
12const prerender = require('prerender')
const server = prerender({
followRedirects: true,
chromeLocation: '/usr/bin/google-chrome',
chromeFlags: [ '--no-sandbox', '--headless', '--disable-gpu', '--remote-debugging-port=9222', '--hide-scrollbars' ],
})
server.use(prerender.blockResources())
server.use(prerender.removeScriptTags())
// server.use(require('prerender-memory-cache'))
server.start()package.json
1
2
3
4
5
6
7
8
9
10
11
12{
"name": "leiem.cn",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"prerender": "5.19.0",
"prerender-memory-cache": "1.0.2"
},
"scripts": {
"start": "node server.js"
}
}
Antd动态控制表单项
再来赞一波 antd 简直不要太好用 ♥️
Antd 的 Form 组件出场率还是非常高的,除了常规的表单各项填写并提交外,有些时候需要根据用户的操作对表单项做一些控制,废话不多说直接拿个例子说
1 | import React from 'react'; |
以上例子会根据用户选择 是否喜欢
项,如果选择 否
则会出现新的表单项。可以看到这里主要使用了 shouldUpdate
属性,默认为 false
。当其值为 true
时则整个表单任何改动都会触发改项的重新渲染,其值也可以为一个函数,例如 shouldUpdate={(p, c) => p.is_love != c.is_love}
则意味着只有 is_love
的值发生变化时才会触发重新渲染。
去除antd蓝色边框
先来赞一波 antd 简直不要太好用 ♥️
某些特殊情况下我们会隐藏例如 Input
或 Select
等组件的边框,边框很好隐藏,找到对应的 class
覆盖样式即可,但隐藏了边框会发现在获取焦点的时候还是会有一个淡蓝色的边框就像这样。
下边分别介绍几个组件的边框隐藏方法。
Input 组件
通过观察可以发现有个 focus
的伪类中的 box-shadow
属性的效果
知道了原因解决方法也很简单
1 | :global(.ant-input):focus { |
这里我使用了 CSS Modules 所以外层加了 :global
,大家可根据情况处理。
Select / Cascader / AutoComplete 组件
这几个组件表现形式相似,同样也是通过 box-shadow
属性实现的
直接覆盖即可
1 | :global(.ant-select-selector) { |
InputNumber 组件
1 | :global(.ant-input-number-focused) { |
DatePicker 组件
1 | :global(.ant-picker-focused) { |
Django ORM ManyToManyField
Django
官网对 ManyToManyField
操作的例子写的比较少,正好用到了就来总结下,先来定义下 Model
1 | from django.db import model |
如果执行 manage.py migrate
后会自动多生成一个 companies_products
的关系表。
关系维护
创建关联关系
1
2
3
4
5
6
7company = Company.objects.get(pk=1)
company.products.add(1)
company.products.add(2, 3)
# 反向操作
product = Product.objects.get(pk=1)
product.company_set.add(1)
product.company_set.add(2, 3)设置关联关系
1
2company.products.set([2, 3])
product.company_set.set([1, 3])关联创建
1
2company = Company.objects.get(pk=1)
company.products.create(name='Model 3')移除关联关系
1
2company.products.remove(1, 3)
product.company_set.remove(2)清除关联关系
1
2company.products.clear()
product.company_set.clear()
关联查询
查询某公司关联的所有产品
1
2company = Company.objects.get(pk=1)
company.products.all()等价于
1
Product.objects.filter(company__id=1)
查询拥有某产品的所有公司
1
2product = Product.objects.get(pk=1)
product.company_set.all()等价于
1
Company.objects.filter(products__id=1)
获取关系表对象
如上例子可以通过 Company.products.through
获取关系表对象,例如
1 | rel = Company.products.through.objects.first() |
Typora图片自动上传阿里云OSS
Typora
是非常好用的所见即所得的 Markdown 编辑器,关于图片上传支持自定义写脚本来上传,这就给予我们很大的灵活性,我们就自己动手写个脚本来把图片自动上传到阿里云的OSS里。
创建上传脚本
以下既是脚本内容,要根据自己的 OSS 配置稍作更改,这里使用了阿里云官方的 OSS命令行工具
1 |
|
配置Typora
直接上截图了,插入图片时将会自动上传到阿里云OSS
禁用mac输入首字母大写
Mac 系统自带的输入法默认会在你输入英文时自动提示首字母大写,不管里这时候敲回车还是空格,都会自动转成建议的首字母大写,就像这样
正常编辑文章估计感觉这个特性还蛮好的,但像我们这写代码文档什么的就感觉非常不方便太耽误事儿了,关闭方法也很简单直接上截图了