Python 网站开发常用的框架有 Flask, Django 等等,其中 Django 适合大型网站的开发,Flask 是一种微框架,用户自定义程度高。本篇介绍 Flask 开发网站的一些知识。

Flask 安装

建议安装 Miniconda,并创建相应 Python 版本的虚拟环境,然后通过如下命令安装 Flask

1
pip install Flask

Flask-SQLAlchemy 数据库

添加用户和删除用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from app.models import User
from app import bcrypt

# 明文密码
passwd = '11111111'
# 添加用户时必须使用加密密码
me = User(username='me', email='me@outlook.com', password=bcrypt.generate_password_hash(passwd))

# 添加用户
db.session.add(me)
# 提交后才算成功
db.session.commit()

# 删除用户。特别注意,当用户发布有 post 时,必须先删除所有的 post,才可以删除用户。
db.session.delete(me)
# 提交后才算成功
db.session.commit()

查找用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查找知道的用户名的用户
u = User.query.filter_by(username='peter').first()

# 查找邮件以 @example.com 结尾的用户
User.query.filter(User.email.endswith('@example.com')).all()

# 对用户名排序
User.query.order_by(User.username).all()

# 查ID前两个的用户
User.query.limit(2).all()

# 查ID是1的用户,注意ID以1开始
User.query.get(1)

如何为用户更改密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 导入类,我这里都写着 app.models 模块中
from app.models import User, db
from app import bcrypt

# 查询所有用户
User.query.all()
[<User 'jinzhongxu'>]

# 筛选特定用户
u = User.query.filter(User.username=='jinzhongxu').first()

# 明文密码
newpasswd = '00000000'
# 数据库中存储的是加密后的密码
u.password = bcrypt.generate_password_hash(newpasswd)
# 更改后记得提交
db.session.commit()

# 登录认证时,使用如下的方法比较加密后的密码。注意,每次生成的加密密码不同,但可校验密码正确性。
bcrypt.check_password_hash(u.password, newpasswd)
True

删除博文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 筛选用户
u = User.query.filter(User.username=='jinzhongxu').first()
# 查看博文总数
u.posts
# 0 表示最老的博文
u.posts[0]
# 删除最老的博文
db.session.delete(u.posts[0])
# 记得提交才算成功
db.session.commit()

# 使用 u.posts.remove(u.posts[0]) 和 db.session.commit() 出现问题(IntegrityError: (sqlite3.IntegrityError) NOT NULL constraint failed: post.user_id)后修复
# 当错误方法删除 post,或没有删除用户的 post,就直接删除用户都会遇到该错误
db.session.rollback()

前端使用 python 函数

在前端网页开发时,常需要调用python的内置函数,如 os, datetime, len 等,可以使用如下的方法,在 app 模块中

1
2
3
4
5
from flask import Flask
import datetime

app = Flask(__name__)
app.jinja_env.globals['datetime'] = datetime

这样,在 templates 的 所有 HTML 中就可以直接调用了,如下面的示例

1
2
3
4
5
6
7
8
9
10
<div class="media-body">
<h4 class="media-heading">{{ post.author.username }} </h4>
<small class="text-muted">{{ post.timestamp + datetime.timedelta(hours=8) }} </small>
<p>{{ post.body }}</p>
</div>

<div>
{{ datetime.datetime.utcfromtimestamp(os.stat(os.path.join('app', tools)).st_mtime + 28800).strftime("%Y-%m-%d %H:%M:%S.%f") }},
{{ os.path.getsize(os.path.join('app', tools)) }}
</div>

默认,post.timestamp 的时区是 Coordinated Universal Time (UTC),想要改成 China Standard Time (CST),需要增加 8 小时。因 post.timestamp 为 datetime.datetime 对象,所以可以直接在其上累加时间 post.timestamp + datetime.timedelta(hours=8)

离线使用 Flask-Bootstrap

当想要把 flask 网站部署到内网机器上时,除了下载所有需要的依赖包,如果使用了 Flask-Bootstrap,还需要设置如下内容,才可以正确渲染导航栏

1
2
3
vim /usr/local/miniconda/lib/python3.9/site-packages/flask_bootstrap/__init__.py
# 更改如下的内容,把 False 改为 True
app.config.setdefault('BOOTSTRAP_SERVE_LOCAL', True)

最好的方法是将该语句增加到 app.py 文件中,而不更改按照的 Flask-Bootstrap 源文件:

1
app.config.setdefault('BOOTSTRAP_SERVE_LOCAL', True)

跳转到外部链接页面

在当前页面打开新链接页面,会覆盖当前页面

1
2
3
<li>
<a href="https://xujinzh.github.io/" >JBlog</a>
</li>

点击链接另外创建新页面,不覆盖当前页面

1
2
3
<li>
<a href="https://xujinzh.github.io/" target="_blank">JBlog</a>
</li>

HTML 保留小数的有效位

1
2
3
4
5
6
7
8
# 格式化方法
<div>Size: {{ '%0.2f' % (os.path.getsize('app/' + videos) / 1024 / 1024) | float }} MB</div>

# 简便方法
<div>Size: {{ (os.path.getsize('app/' + videos) / 1024 / 1024) | round(2)}} MB</div>

# 保留整数,round 默认为 x.0 的形式,注意与保留一位小数的区别
<div>Size: {{ (os.path.getsize('app/' + videos) / 1024 / 1024) | round | int }} MB</div>

多进程和多线程

flask 1.0 以后默认开启多线程,即

1
2
3
app.run()
# 等价于
app.run(threaded=True)

多线程可以让多客户端访问,各子线程共享全局变量。但只使用一个进程,无法利用多CPU的资源。

flask 中可以使用多进程

1
app.run(processes=True)

多进程可以使用多个CPU,但不能共享全局变量,对资源的开销比较大。各子进程都从主进程复制代码。

FLASK 中多进程和多线程只能选择其一

参考链接

  1. 在Python的Flask框架中使用日期和时间的教程
  2. flask-sqlalchemy官网:Select, Insert, Delete
  3. html:打开新的页面
  4. flask 多进程/多线程 解决高并发问题