Flask-3实战高阶
环境搭建虚拟环境pycharm连接SSH 连接远程环境教程:设置连接虚拟环境选择虚拟环境的解释器:/root/workspaces/flask/bin/python博客迁移打包依赖(flask) C:\Users\q2723\PycharmProjects\flaskProject9>pip freeze >requirements.txt建虚拟环境mkvirtualenv myblo
环境搭建
虚拟环境pycharm连接
SSH 连接远程环境
设置
连接虚拟环境
选择虚拟环境的解释器:/root/workspaces/flask/bin/python
博客迁移
打包依赖
(flask) C:\Users\q2723\PycharmProjects\flaskProject9>pip freeze >requirements.txt
建虚拟环境
mkvirtualenv myblog
(myblog) [root@izbp1i7e0dqxcb89vkdgc3z ~]# which python
/root/workspaces/myblog/bin/python
[root@izbp1i7e0dqxcb89vkdgc3z tmp]# mkdir myproject
[root@izbp1i7e0dqxcb89vkdgc3z tmp]# cd myproject/
[root@izbp1i7e0dqxcb89vkdgc3z myproject]# mkdir flask_blog
[root@izbp1i7e0dqxcb89vkdgc3z myproject]# pwd
/tmp/myproject
设置 ssh 配置
他会自动同步
安装依赖
(myblog) [root@izbp1i7e0dqxcb89vkdgc3z flask_blog]# pip install -r requirements.txt
(myblog) [root@izbp1i7e0dqxcb89vkdgc3z flask_blog]# pip list
Package Version
---------------- ---------
alembic 1.4.3
antiorm 1.2.1
certifi 2020.12.5
chardet 4.0.0
.........
配置数据库
可以删除原来的 migration 文件夹
原来的项目没有设置 host
可以在启动的时候,加 -h
python app.py runserver -h 0.0.0.0
使用navicat迁移数据库(可选)
需要提前在服务器建好同名数据库
我的阿里云服务器已经安装mysql5.7
[root@izbp1i7e0dqxcb89vkdgc3z ~]# cd /usr/local/mysql
[root@izbp1i7e0dqxcb89vkdgc3z mysql]# mysql
-bash: mysql: command not found
[root@izbp1i7e0dqxcb89vkdgc3z mysql]# alias mysql=/usr/local/mysql/bin/mysql
[root@izbp1i7e0dqxcb89vkdgc3z mysql]# mysql
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
[root@izbp1i7e0dqxcb89vkdgc3z mysql]# mysql -u root -p
Enter password:
[root@izbp1i7e0dqxcb89vkdgc3z mysql]# sudo grep mysql_root_passwd /root/env.txt
mysql_root_passwd:xxxxxxxxx
mysql> create database flask;
Query OK, 1 row affected (0.00 sec)
#因为原来model文件article content数据类型不对
用mysql 语句修改
mysql> alter table article modify column content blob
-> ;
Query OK, 2 rows affected (0.06 sec)
Records: 2 Duplicates: 0 Warnings: 0
允许远程连接mysql
mysql> use mysql;
Database changed
mysql> select host from user where user='root';
+-----------+
| host |
+-----------+
| localhost |
+-----------+
1 row in set (0.00 sec)
mysql> SET SQL_SAFE_UPDATES = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> update user set host = '%' where user ='root';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> SET SQL_SAFE_UPDATES = 1;
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
需要更改 settings.py 文件
nohup命令说明:
用途:不挂断地运行命令。
语法:nohup Command [ Arg … ] [ & ]
不建议使用
Nginx
Nginx 是一个高性能的 HTTP 和方向代理服务器,也是一个 IMAP/POP3/SMTP 服务器
Nginx 也是一个轻量级服务器(Tomcat 属于重量级,Java 一般部署在 Tomcat)
Apache、Tomcat、Nginx
Tomcat:应用(Java)服务器,只是一个 Servlet 容器,可以认为是 Apache 的扩展,可以独立于 Apache 运行
Apache:
Nginx:
- 俄罗斯人编写轻量级 HTTP 服务器,发音‘engine X’
- 抗并发,nginx 处理请求是异步非阻塞
- 高度模块化
- 反向代理,接受外网请求,转发给内网服务器,再将结果返回给外网请求的客户端
- 负载均衡,通过方向代理服务器来优化网站的负载
作者:嘶吼
这三者统一的功能是都有网络代理服务,我想侧重于先说一下nginx。然后因为apache和tomcat都是以由apache组织开发,所以我会从几个方面来看看nginx与apache的部分区别。
Nginx
nginx是由一位来自俄罗斯的程序员Igor Sysoe所编写的十分轻量级的HTTP服务器。nginx,它的发音为“engine X”。是一个高性能基于HTTP和反向代理的服务器,当只有静态资源的时候,就可以使用nginx来做服务器,现在很流行的动静分离(普遍情况下,是需要动态资源和静态资源分开,将静态资源部署在nginx上,当如果是静态资源的请求来时,就直接到nginx配置的静态资源目录下面获取资源;如果是动态资源的请求,nginx利用反向代理的原理,把请求转发给后台应用去处理,从而实现动静分离)就能通过nginx来实现。
· 具备高性能反向代理服务并属于轻量级的web服务器
反向代理
简单说就是利用代理服务器来接受互联网上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给互联网上请求连接的客户端,此时的代理服务器对外表现就属于一个反向代理服务器。
· 良好的扩展性,可以通过模块方式来进行功能扩展
· 较为灵活的负载均衡策略
轮询(默认)算法:每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某台服务器宕机,则自动剔除故障机器,使用户访问不受到影响,个人觉得这个是最好的方法,但也有成本就是消耗机器的成本!实际情况中也验证了这一点,移动段的token!
· 抗并发的能力很强
Apache
Apache HTTP服务器是一个模块化的服务器,可以运行在几乎所有广泛使用的计算机平台上。其属于应用服务器。Apache支持支持模块多,性能稳定,Apache本身是静态解析,适合静态HTML、图片等,但可以通过扩展脚本、模块等支持动态页面等。
Tomcat
是应用(Java)服务器,它只是一个Servlet(JSP也翻译成Servlet)容器,可以认为是Apache的扩展,但是可以独立于Apache运行。
如果请求是静态网页则由Apache处理,并将结果返回;如果是动态请求,Apache会将解析工作转发给Tomcat处理,Tomcat处理后将结果通过Apache返回。这样可以达到分工合作,实现负载均衡,提高系统的性能。
Apache和Nginx的部分功能相比较
异步能力:apache中也有异步模块支持异步功能,不过是阻塞性异步,而nginx是非阻塞性异步。
抗并发:nginx因为软件体积小,消耗主机资源少,抗并发能力是apache的3倍以上。
重量等级:apache配置相对nginx复杂,自身不支持动态页面。
漏洞缺陷:apache相对nginx的bug会少很多。
Nginx 以轻量的优点在慢慢取带重量级的Apache ,现在已经是主流。
正反向代理
- 正向代理:翻墙就是正向代理
- 反向代理:服务器位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,即用户直接访问反向代理服务器就可以获得目标服务器的资源。同时,用户不需要知道目标服务器的地址,也无须在用户端作任何设定
安装
https://my.oschina.net/yueshengwujie/blog/3099219
#查找安装路径,默认都是这个路径
[root@izbp1i7e0dqxcb89vkdgc3z nginx-1.18.0]# whereis nginx
nginx: /usr/local/nginx
-
nginx 端口80
/usr/local/nginx
开机自启
-
apache 端口 8080
cd /usr/local/apache
开机自启
systemctl问题解决
# 问题
[root@izbp1i7e0dqxcb89vkdgc3z sbin]# systemctl restart ./nginx
Failed to restart .-nginx.service: Unit not found.
# 解决方法
在/etc/init.d/目录下新建文件,文件名为nginx;或者用命令在根目录下执"行:# vim /etc/init.d/nginx (注意vim旁边有一个空格),随后插入代码:
#!/bin/sh
# nginx - this script starts and stops the nginx daemin
#
# chkconfig: - 85 15
# description: Nginx is an HTTP(S) server, HTTP(S) reverse \
# proxy and IMAP/POP3 proxy server
# processname: nginx
# config: /usr/local/nginx/conf/nginx.conf
# pidfile: /usr/local/nginx/logs/nginx.pid
# Source function library.
. /etc/rc.d/init.d/functions
# Source networking configuration.
. /etc/sysconfig/network
# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0
nginx="/usr/local/nginx/sbin/nginx"
prog=$(basename $nginx)
NGINX_CONF_FILE="/usr/local/nginx/conf/nginx.conf"
lockfile=/var/lock/subsys/nginx
start() {
[ -x $nginx ] || exit 5
[ -f $NGINX_CONF_FILE ] || exit 6
echo -n $"Starting $prog: "
daemon $nginx -c $NGINX_CONF_FILE
retval=$?
echo
[ $retval -eq 0 ] && touch $lockfile
return $retval
}
stop() {
echo -n $"Stopping $prog: "
killproc $prog -QUIT
retval=$?
echo
[ $retval -eq 0 ] && rm -f $lockfile
return $retval
}
restart() {
configtest || return $?
stop
start
}
reload() {
configtest || return $?
echo -n $"Reloading $prog: "
killproc $nginx -HUP
RETVAL=$?
echo
}
force_reload() {
restart
}
configtest() {
$nginx -t -c $NGINX_CONF_FILE
}
rh_status() {
status $prog
}
rh_status_q() {
rh_status >/dev/null 2>&1
}
case "$1" in
start)
rh_status_q && exit 0
$1
;;
stop)
rh_status_q || exit 0
$1
;;
restart|configtest)
$1
;;
reload)
rh_status_q || exit 7
$1
;;
force-reload)
force_reload
;;
status)
rh_status
;;
condrestart|try-restart)
rh_status_q || exit 0
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
exit 2
esac
# 接下来就依次操作以下命令:
cd /etc/init.d
chmod 755 /etc/init.d/nginx
chkconfig --add nginx
nginx控制
这样解决之后可以通过系统管理
systemctl status nginx 查看nginx状态
systemctl start nginx 启动
systemctl stop nginx 关闭
systemctl enable nginx 设置开机自启
systemctl disable nginx 禁止开机自启
或者进入 /usr/local/nginx 目录通过文件控制
配置文件
其他:
需要改 user 和 localtion
#进入nginx配置文件目录,找到nginx的配置文件nginx.conf
cd /usr/local/nginx/conf/
#直接修改
vi nginx.conf
#nginx.conf
[root@izbp1i7e0dqxcb89vkdgc3z conf]# cat nginx.conf
user root;
worker_processes 1;
# 需要改user为当前登录用户
# 主进程
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
# 支持文件类型
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
# 日志格式,保存位置
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
# 网站根目录
#error_page 404 /404.html;
[root@izbp1i7e0dqxcb89vkdgc3z conf]# cat mime.types
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/javascript js;
application/atom+xml atom;
application/rss+xml rss;
......
uWSGI
WSGI
- WEB 服务网关接口
- 不同的框架遵循的协议标准规范
- 表述 web server 如何于 web application 通信的规范
uwsgi
- 和 WSGI 一样是一种通信协议,是 uWSGI 服务器的独占协议
- 用于定义传输信息的类型
- 每一个 uwsgi packet 的前 4byte 为传输信息类型的描述,速度较快
uWSGI
- uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http等协议
- Nginx中HttpUwsgiModule的作用是与uWSGI服务器进行交换
- WSGI是一种Web服务器网关接口
- 它是一个Web服务器(如nginx,uWSGI等服务器)与web应用(如用Flask框架写的程序)通信的一种规范
Django,Flask 等框架都有自己的就简单 WSGI server,一般用于服务器调试,生产环境使用其他 WSGI server
nginx作用
配置 uWSGI
安装
(myblog) [root@izbp1i7e0dqxcb89vkdgc3z ~]# pip install uwsgi
配置
工程目录下创建 uwsgi.ini
(myblog) [root@izbp1i7e0dqxcb89vkdgc3z flask_blog]# touch uwsgi.ini
uwsgi.ini 文件
[uwsgi]
#指定IP端口 // 直接外部访问
#使用nginx连接时,使用
#socket = 0.0.0.0:2333
#直接作为web服务器时,使用
http = 0.0.0.0:80
#项目目录
chdir = /tmp/myproject/flask_blog
#适用于flask项目部署
wsgi-file = app.py
#router
callable = app
#进程个数
processes = 5
pidfile = uwsgi.pid
#启用线程
enable-threads = true
#启用主进程
master = true
#设置日志目录
daemonize = uwsgi.log
#####################
简单项目可以只用以上部分
#指定项目的application
module = web.wsgi:application
#uwsgi启动用户名和用户组
uid = www
gid = www
#指定Socket路径 //内部访问 #权限和nginx一样
socket = /www/wwwroot/web/script/uwsgi.sock
#socket = :8080
#socket权限设置
chmod-socket = 755
#自动移除unix Socket 和 Pid 文件 当服务停止的时候
vacuum = true
#序列化接受的内容,如果可能的话
thunder-lock = true
#设置自动中断时间
harakiri = 30
#设置缓冲
post-buffering = 4096
#设置静态文件
#static-map = /static=//www/wwwroot/mysite/static
使用 uWSGI 服务器
启动: uwsgi --ini uwsgi.ini
停止: uwsgi --stop uwsgi.pid
#(myblog) [root@izbp1i7e0dqxcb89vkdgc3z flask_blog]# uwsgi --ini uwsgi.ini
[uWSGI] getting INI configuration from uwsgi.ini
(myblog) [root@izbp1i7e0dqxcb89vkdgc3z flask_blog]# ps -aux |grep uwsgi
root 2403 15.3 2.2 263304 41456 ? S 19:38 0:00 uwsgi --ini uwsgi.ini
root 2406 0.0 1.9 263304 36468 ? S 19:38 0:00 uwsgi --ini uwsgi.ini
root 2407 0.0 1.9 263304 36468 ? S 19:38 0:00 uwsgi --ini uwsgi.ini
root 2408 0.0 1.9 263304 36468 ? S 19:38 0:00 uwsgi --ini uwsgi.ini
root 2409 0.0 1.9 263304 36468 ? S 19:38 0:00 uwsgi --ini uwsgi.ini
root 2410 0.0 1.9 263304 36468 ? S 19:38 0:00 uwsgi --ini uwsgi.ini
root 2412 0.0 0.0 112812 972 pts/0 R+ 19:38 0:00 grep --color=auto uwsgi
#当前文件夹内就会生成
uwsgi.log
uwsgi.pid
(myblog) [root@izbp1i7e0dqxcb89vkdgc3z flask_blog]# cat uwsgi.pid
28374
用于停止服务时使用
问题解决
#(myblog) [root@izbp1i7e0dqxcb89vkdgc3z flask_blog]# curl 127.0.0.1
curl: (7) Failed connect to 127.0.0.1:80; Connection refused
#(myblog) [root@izbp1i7e0dqxcb89vkdgc3z flask_blog]# netstat -anptl
Active Internet connections (servers and established)
tcp 0 0 172.17.28.202:45708 100.100.30.26:80 ESTABLISHED 1166/AliYunDun
# 就是你端口没有服务的话,阿里云盾会自动占用
(myblog) [root@izbp1i7e0dqxcb89vkdgc3z flask_blog]# uwsgi --ini uwsgi.ini
[uWSGI] getting INI configuration from uwsgi.ini
#启动成功
动静分离
nginx.conf 拷贝到当前项目下
http{
include /usr/local/nginx/conf/mime.types;
........
root /tmp/myproject/flask_blog;
location / {
include /usr/local/nginx/conf/uwsgi_params;
uwsgi_pass localhost:2333;
}
# 动态 连接 uwsgi
location /static{
alias /root/myproject/flask_blog/static;
}
#静态
.........
}
[root@izbp1i7e0dqxcb89vkdgc3z sbin]# nginx -c /tmp/myproject/flask_blog/nginx.conf
#启动用绝对路径
只使用 systemctl 无法指定 conf 文件
uwsgi.ini
#关闭http = 0.0.0.0:80
socket = 0.0.0.0:2333
ls
(myblog) [root@izbp1i7e0dqxcb89vkdgc3z flask_blog]# ls
app.py ext nginx-1.18.0.tar.gz nginx.conf requirements.txt static uwsgi2.ini uwsgi.log
apps migrations nginx2.conf __pycache__ settings.py templates uwsgi.ini uwsgi.pid
Redis
使用 Redis 对数据进行缓存
安装
centos7
-
安装gcc依赖
由于 redis 是用 C 语言开发,安装之前必先确认是否安装 gcc 环境(gcc -v),如果没有安装,执行以下命令进行安装
[root@localhost local]# yum install -y gcc
-
下载并解压安装包
[root@localhost local]# wget http://download.redis.io/releases/redis-5.0.3.tar.gz [root@localhost local]# tar -zxvf redis-5.0.3.tar.gz
3. cd切换到redis解压目录下,执行编译
````bash
[root@localhost local]# cd redis-5.0.3
[root@localhost redis-5.0.3]# make
-
安装并指定安装目录
[root@localhost redis-5.0.3]# make install PREFIX=/usr/local/redis
-
启动服务
5.1前台启动
[root@localhost redis-5.0.3]# cd /usr/local/redis/bin/ [root@localhost bin]# ./redis-server
5.2后台启动
从 redis 的源码目录中复制 redis.conf 到 redis 的安装目录
````bash
[root@localhost bin]# cp /usr/local/redis-5.0.3/redis.conf /usr/local/redis/bin/
修改 redis.conf 文件,把 daemonize no 改为 daemonize yes
[root@localhost bin]# vi redis.conf
后台启动
[root@localhost bin]# ./redis-server redis.conf
-
设置开机启动
添加开机启动服务
[root@localhost bin]# vi /etc/systemd/system/redis.service
复制粘贴以下内容:
[Unit] Description=redis-server After=network.target [Service] Type=forking ExecStart=/usr/local/redis/bin/redis-server /usr/local/redis/bin/redis.conf PrivateTmp=true [Install] WantedBy=multi-user.target
注意:ExecStart配置成自己的路径
设置开机启动
[root@localhost bin]# systemctl daemon-reload [root@localhost bin]# systemctl start redis.service [root@localhost bin]# systemctl enable redis.service
创建 redis 命令软链接
[root@localhost ~]# ln -s /usr/local/redis/bin/redis-cli /usr/bin/redis
测试 redis
服务操作命令
systemctl start redis.service #启动redis服务
systemctl stop redis.service #停止redis服务
systemctl restart redis.service #重新启动服务
systemctl status redis.service #查看服务当前状态
systemctl enable redis.service #设置开机自启动
systemctl disable redis.service #停止开机自启动
Redis 基础知识
https://www.runoob.com/redis/redis-tutorial.html
五种数据类型
- string 字符串
- hash 哈希(键值对)
- list 列表
- set 集合
- zset 有序集合(常用于排序)
SET name value
GET value
虚拟环境安装 redis 插件
(myblog) [root@izbp1i7e0dqxcb89vkdgc3z ~]# pip install redis
Flask-caching
为了尽量减少缓存穿透,同时减少web的响应时间,可以针对那些需要一定时间才能获取结果的函数和那些不需要频繁更新的视图函数提供缓存服务,可以在一定的时间内直接返回结果而不是每次都需要计算或者从数据库中查找
(myblog) [root@izbp1i7e0dqxcb89vkdgc3z ~]# pip install flask-caching
引入项目
ext/__init__.py
from flask_caching import Cache
cache = Cache()
apps/__init__.py
......
from ext import db, bootstrap, cache
config = {
'CACHE_TYPE': 'redis',
'CACHE_REDIS_HOST': '127.0.0.1',
'CACHE_REDIS_PORT': 6379
}
# 配置 redis 数据库
def create_app():
app = Flask(__name__, template_folder='../templates', static_folder='../static')
app.config.from_object(settings.ProductionConfig)
cache.init_app(app=app, config=config)
# 初始化缓存文件
......
缓存键值对
设置:
cache.set(key,value,timeout=second)
cache.set_many([key,value],[key,value].......)
获取:
cache.get(key)
cache.get_many(key1,key2......)
删除:
cache.delete(key)
cache.delete_many(key1,key2......)
cache.clear()
手机验证码就可以使用 缓存键值对的形式 保存校验
缓存视图函数
@user_bp.route('/', endpoint='index')
@cache.cached(timeout=50)
def index():
......
# @cache.cached(timeout=50)
timeout 设置过期时间
直接加到对应路由上,适用于某一页面内容较多刷新速度较慢时
与Flask-caching类似的插件:Flask-cache(较少用)
WTForms
开发文档:http://www.pythondoc.com/flask-wtf/
Flask-WTF是集成WTForms,并带有 CSRF 令牌的安全表单和全局的 CSRF 保护功能,在建立表单所创建的类都是继承 Flask_wtf 中的 FlaskForm,而 FlaskForm 是继承 WTForms 中的 forms
功能
- 集成 wtforms。
- 带有 csrf 令牌的安全表单。
- 全局的 csrf 保护。
- 支持验证码(Recaptcha)。
- 与 Flask-Uploads 一起支持文件上传。
- 国际化集成。
安装
[root@izbp1i7e0dqxcb89vkdgc3z ~]# pip install Flask-WTF
表单
使用方式类似于数据库
只提供表单所以要加 form 标签
使用 wtform 必须设置 SECRET_KEY
标准表单字段
TextField 代表<input type ='text'> HTML表单元素
IntegerField 用于显示整数的TextField
TextAreaField 代表<testarea> html表单元素
PasswordField 代表<input type ='password'> HTML表单元素
SubmitField 表示<input type ='submit'>表单元素
SelectField 表示选择表单元素
StringField
PasswordField
DecimalField
BooleanField
DatetimeField
......
常用验证器
DataRequired 检查输入栏是否为空
mail 检查字段中的文本是否遵循电子邮件ID约定
IPAddress 验证输入字段中的IP地址
Length 验证输入字段中字符串的长度是否在给定范围内
NumberRange 在给定范围内的输入字段中验证一个数字
URL 验证输入字段中输入的URL
EqualTO
# 添加方法
name = TextField("Name Of Student",[validators.Required("Please enter your name.")])
使用
- 引入 CSRF
# __init__.py
from flask_wtf import CSRFProtect
def create_app():
......
CSRFProtect.init_app(app=app)
......
- 定义 Form.py
from flask_wtf import Form, validators
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired, Length
class UserForm(Form):
username = StringField('name', validators=[DataRequired()])
password = PasswordField('password', validators=[Length(min=6, max=20, message='长度必须在6~20位之间')])
# 此表有 username和password两个字段,类似于sqlalchemy
- 使用
视图函数
@article_bp1.route('form', methods=["GET", "POST"], endpoint='form')
def form():
userform = UserForm()
# 进行校验
if userform.validate_on_submit():
return 'ok'
return render_template('article/form.html', userform=userform)
模板中
<form method="POST" action="{{ url_for('article.form')}}">
{{ userform.csrf_token }}
{# 防止csrf,必须设置secret_key #}
{{ userform.username }}{% if userform.username.errors %}{{ userform.username.errors.0 }}{% endif %}
{# 如果有报错则输出报错的message, .0表示只输出内容 #}
{{ userform.password }}{% if userform.password.errors %}{{ userform.password.errors.0 }}{% endif %}
<input type="submit" value="Go">
</form>
#如果模板中存在表单,你不需要做任何事情。与之前一样:
<form method="post" action="/">
{{ form.csrf_token }}
</form>
# 但是如果模板中没有表单,你仍然需要一个 CSRF 令牌:
<form method="post" action="/">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
</form>
自定义验证
# form.py
class UserForm(FlaskForm):
username = StringField(label='用户名', validators=[DataRequired(), Length(min=6, max=20, message='长度必须在6~20位之间')])
password = PasswordField(label='密码', validators=[DataRequired(), Length(min=6, max=20, message='长度必须在6~20位之间')])
confirm_password = PasswordField('确认密码', validators=[DataRequired(), Length(min=6, max=20, message='长度必须在6~20位之间'),EqualTo('password', '密码不一致')])
phone = StringField('手机号', validators=[DataRequired(), Length(min=11, max=11, message='长度必须是11位')])
email = EmailField('邮箱', validators=[DataRequired()])
def validate_username(self, data):
# validata_(与上边定义的字段名相同)
if self.username.data[0].isdigit():
raise ValidationError('用户名不能以数字开头')
def validate_phone(self, data):
phone = data.data
# 如果匹配上就返回一个对象,没匹配就返回 None
if not re.search(r'^1[356789]\d{9}$', phone):
raise ValidationError('手机号格式不正确')
# 注意使用 raise 关键字而非 return
form.html
<form method="POST" action="{{ url_for('article.form')}}">
{{ userform.csrf_token }}
{# 防止csrf,必须设置secret_key #}
{{ userform.username.label }}:{{ userform.username }}{% if userform.username.errors %}{{ userform.username.errors.0 }}{% endif %}<br>
{# 如果有报错则输出报错的message, .0表示只输出内容 #}
{{ userform.password.label }}:{{ userform.password }}{% if userform.password.errors %}{{ userform.password.errors.0 }}{% endif %}<br>
{{ userform.confirm_password.label }}:{{ userform.confirm_password }}{% if userform.confirm_password.errors %}{{ userform.confirm_password.errors.0 }}{% endif %}<br>
{{ userform.phone.label }}:{{ userform.phone }}{% if userform.phone.errors %}{{ userform.phone.errors.0 }}{% endif %}<br>
{{ userform.email.label }}:{{ userform.email }}{% if userform.email.errors %}{{ userform.email.errors.0 }}{% endif %}<br>
<input type="submit" value="Go">
文件上传
FileField
接受一个 FileStorage
-
定义form
使用 FileField,如果要指明上传类型需要使用:FileAllowed([‘jpg’,‘png’])
-
模板中的使用和其它类型字段一致,但是必须在 form 上面加:enctype=“multipart/form-data”
-
视图函数中如果验证成功
file=uform.data
验证码
- 使用 Flask-WTF 内置验证码
recaptcha
使用 Google 验证器,国内不太友好
- pillow
安装
pip install -i https://pypi.douban.com/simple/ pillow
使用
utils/captcha.py
import os
import random
from PIL import Image, ImageFont, ImageDraw
# 生成随机颜色,返回 rgb 元组
def get_random_color():
return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
# 生成验证码图片
def generate_image(length):
s = 'qazwPLMTRDXZSENBHUJCFhnujmikol098IYGNBHUJIYGV12NBHUJIYGVCF365478WAQ'
size = (130, 50)
# 创建画布
im = Image.new('RGB', size, color=get_random_color())
# 创建字体
font = ImageFont.truetype('simfang.ttf', size=30)
print(os.path.curdir)
# 创建 ImageDraw 对象
draw = ImageDraw.Draw(im)
# 绘制验证码
code = ''
for i in range(length):
c = random.choice(s)
code += c
draw.text((5 + random.randint(2, 15) + 20 * i, random.randint(2, 7)), text=c, fill=get_random_color(),
font=font)
# 绘制干扰线
for i in range(8):
x1 = random.randint(0, 130)
y1 = random.randint(0, 50 / 2)
x2 = random.randint(0, 130)
y2 = random.randint(50 / 2, 50)
draw.line(((x1, y1), (x2, y2)), fill=get_random_color())
return im, code
user/view.py
@user_bp.route('/image')
def get_image():
im, code = generate_image(4)
session['valid'] = code
# 将 image 对象转换成二进制
buffer = BytesIO()
im.save(buffer, 'JPEG')
buf_bytes = buffer.getvalue()
response = make_response(buf_bytes)
response.headers['Content-Type'] = 'image/jpg'
return response
注意:字体文件,部署服务器使也需要设置,可以使用命令查找
[root@izbp1i7e0dqxcb89vkdgc3z sbin]# find / -name *.ttf
/usr/share/fonts/dejavu/DejaVuSansCondensed-BoldOblique.ttf
/usr/share/fonts/dejavu/DejaVuSans-ExtraLight.ttf
/usr/share/fonts/dejavu/DejaVuSansCondensed.ttf
/usr/share/fonts/dejavu/DejaVuSans-BoldOblique.ttf
.....
bootstrap结合flask-wtf
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block mycontent %}
<form action="{{ url_for('user.form02') }}" method="post" enctype="multipart/form-data">
{{ wtf.quick_form(uform,button_map={'submit_button':'primary'},horizontal_columns=('sm',1,1)) }}
</form>
{% endblock %}
在大多数flask教程中,都会介绍使用WTForms和bootstrap,使用起来确实比较方便。方法大致如下:
from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, SubmitField from wtforms.validators import DataRequired class LoginForm(FlaskForm): name = StringField('姓名', validators=[DataRequired()]) password = PasswordField('密码', validators=[DataRequired()]) submitfield = SubmitField('提交') 12345678
在模版中各表单项分开渲染:
<form id="loginform" action="/login" method="post"> {{ form.hidden_tag() }} {{ form.name.label }}{{ form.name }} {{ form.password.label }}{{ form.password }} {{ form.submitfield() }} </form> 123456
或者使用quick_form()渲染:
{% import 'bootstrap/wtf.html' as wtf %} {{ wtf.quick_form(form) }} 12
对于WTForms大概也是如此介绍,一般情况下也能用。但是如果我想把表单的检验放在前端呢?如何下手?渲染时若使用第一种方法,那还可以在
<form>
中设置id、action之类的;但若使用的是quick_form()呢?你想修改一点什么东西都无从下手。百度中也很少有人提及这个问题,查了不少资料,总算摸出点头绪来,解决了自己的问题。在各种Field的定义中,可以加入一个
render_kw
。这是一个字典,平常HTML各种定义都可以放在里边。比如:class LoginForm(FlaskForm): name = StringField('姓名', render_kw={"id":"name", "placeholder":"请输入用户名"}) password = PasswordField('密码', render_kw={"id":"pwd", "placeholder":"请输入密码"}) submitfield = SubmitField('提交', render_kw={"type": "button", "onclick":"alert('提交')"}) 1234
这样子就可以在点击
提交
按钮后调用自定义的js函数进行检验,若满足条件再submit()。(上例中只是使用alert()弹出一个窗口。注意:在SubmitField中,需要加上"type": "button",要不然就算js函数中检验未通过不想submit(),它最终依然还是会自己提交表单。
----其实加上"type": "button"的SubmitField,已经可以当作普通的button来使用了。)但是在前端中调用submit()需要用到form对象,而获取form对象需要定义form的id,再使用var form = document.getElementById("loginform");
。但quick_form(form)渲染时是没有id的。这就需要在quick_form()中加入一些参数:{{ wtf.quick_form(form, id='loginform', action='/login', form_type='horizontal', horizontal_columns=('lg',4,4)) }}
id和action意义很明显了。form_type是指定按什么样的方式渲染form的,horizontal_columns是指定一行几列,这两参数都与bootstrap有关,在原代码中看不太懂,大家修改做下测试,一般能满足到你的要求的。
闪现
message flash
Flask 使用闪现系统向用户反馈信息
flash 内容默认存储到 session 中,所以要提前设置 SECRET_KEY
index.html
{% extends 'base.html' %}
{% block middle %}
{% with messages = get_flashed_messages() %}
<ul>
{% for message in messages %}
<li>
{% if message %}
{{ message }}
{% endif %}
</li>
{% endfor %}
</ul>
{% endwith %}
{% endblock %}
user/view.py
@user_bp.route('/login', methods=['get', 'post'], endpoint='login')
def login():
if request.method == "POST":
username = request.form.get('username')
if username == 'admin':
flash('验证成功!1')
flash('验证成功!2')
flash('验证成功!3')
return render_template('user/index.html')
else:
return render_template('user/login.html')
分类闪现
三种类型
- message(未指定默认为 message)
- error
- warning
- info
接受列表元组
@user_bp.route('/login', methods=['get', 'post'], endpoint='login')
def login():
if request.method == "POST":
username = request.form.get('username')
if username == 'admin':
flash('验证成功!1', 'info')
flash('验证成功!2', 'warning')
flash('验证成功!3', 'error')
return render_template('user/index.html')
index.html
{% block middle %}
{% with messages = get_flashed_messages(with_categories=True) %}
<ul>
{% for category,message in messages %}
<li class="{{ category }}">
{% if message %}
{{ message }}
{% endif %}
</li>
{% endfor %}
</ul>
{% endwith %}
{% endblock %}
-
在一个请求结束的时候添加 flash
-
在当前请求中渲染获取或者是下一个请求中可以获取,其他不可以
使用 redirect 依然可以接收到闪现的信息,但第三次请求就接受不到了
flash('验证成功!1', 'info') return redirect(url_for('user.index'))
-
获取闪现内容
get_flash_messages(whit_categories=True)
过滤闪现消息
可选,有针对性地获取对应类型的闪现消息
get_flash_messages(category_filter=['error'])
日志记录
python logging模块
级别排序:CRITICAL > ERROR > WARNING > INFO > DEBUG
import logging # 引入logging模块
logging.basicConfig(level=logging.DEBUG,format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
# logging.basicConfig函数对日志的输出格式及方式做相关配置
# 由于日志基本配置中级别设置为DEBUG,所以一下打印信息将会全部显示在控制台上
…
日志总结
uwsgi --> uwsgi.log
-
使用 app 自带
app.logger.info('') app.logger.debug('') app.logger.warning('') app.logger.error('')
-
通过 logging 进行创建
import logging logger = logging.getLogger('name') # 默认 flask 的名字叫 app
保存到文件
-
logging.basicConfig(level=logging.DEBUG,format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
-
import logging logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) handler = logging.FileHandler('log.txt') handler.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s') handler.setFormatter(formatter) logger.addHandler(handler)
-
使用 logger.info('message')
获取闪现内容
get_flash_messages(whit_categories=True)
过滤闪现消息
可选,有针对性地获取对应类型的闪现消息
get_flash_messages(category_filter=['error'])
日志记录
python logging模块
级别排序:CRITICAL > ERROR > WARNING > INFO > DEBUG
import logging # 引入logging模块
logging.basicConfig(level=logging.DEBUG,format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
# logging.basicConfig函数对日志的输出格式及方式做相关配置
# 由于日志基本配置中级别设置为DEBUG,所以一下打印信息将会全部显示在控制台上
…
日志总结
uwsgi --> uwsgi.log
-
使用 app 自带
app.logger.info('') app.logger.debug('') app.logger.warning('') app.logger.error('')
-
通过 logging 进行创建
import logging logger = logging.getLogger('name') # 默认 flask 的名字叫 app
保存到文件
-
logging.basicConfig(level=logging.DEBUG,format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
-
import logging logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) handler = logging.FileHandler('log.txt') handler.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s') handler.setFormatter(formatter) logger.addHandler(handler)
-
使用 logger.info('message')

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。
更多推荐
所有评论(0)