前面我们使用了一些简单的html脚本样式进行编写html界面,实际情况下的界面往往更加丰富。在flask中,我们可以引入flask-bootstrap这个包,在代码中编写更加丰富的html
步骤
1、引入包并实例化Bootstrap()对象 exts–>init.py中
from flask_bootstrap import Bootstrap
bootstrap = Bootstrap()
2、apps/init.py中进行配置关联
def create_app():
app = Flask(__name__,template_folder='../templates',static_folder='../static')
app.config.from_object(settings.DevelopmentConfig)
# 初始化db
db.init_app(app)
bootstrap.init_app(app)
return app
3、html中继承bootstrap下的base.html 及使用
{% extends "bootstrap/base.html" %}
bootstrap下的base.html中内置的block:
{% block title %} {% endblock %}
{% block navbar %} {% endblock %}
{% block content %} {% endblock %}
{% block styles %} {% endblock %}
{% block scripts %} {% endblock %}
{% block head %} {% endblock %}
{% block body %} {% endblock %}
注册
# 加密密码
pwdHash = generate_password_hash(password)
登录
# 判断密码是否一致 结果---->bool:False True
check_password_hash(pwdHash,password)
会话机制是用来跟踪用户的整个会话,可以在整个会话中保持用户的信息,常用的会话跟踪技术是Cookie和Session,常用于存储用户的登录信息。如登录某个网站,只要会话不结束,在网站中不需要每次重新登录才能展示相关信息以及可执行相关操作
cookie方式
保存:
通过response对象保存
response = redirect(XXX)
response = render_template(XXX)
response = Response()
response = make_response()
response = jsonify()
通过对象调用方法
response.set_cookie(key,value,max_age)
获取:
通过request对象获取:
request.form.get()
request.args.get()
cookie也在request对象中
request.cookies.get(key) ----->value
删除:
通过response对象删除,将浏览器中存储的cookie对应的key删除:
response = redirect(XXX)
response = render_template(XXX)
response = Response()
response = make_response()
response = jsonify()
通过对象调用方法
response.delete_cookie(key)
session方式(是在服务器端存储用户ip信息)
注意:必须要在配置文件中设置: SECRET_KEY=“xxxxx”,添加SECRET_KEY的目的是为了sessionid的加密,不设置会报错
保存:
session['uid'] = user.id
获取:
uid = session.get('uid')
删除:
del session['uid']
在程序中,针对一些经常需要被调用的函数,又不想在代码中进行重复编码的情况,或者实现一些只需要在程序每次重新启动时调用函数的情况下,可以走center路由选择钩子函数。比如对登录权限的验证:走center路由,判断用户是否是登录状态,如果用户登录了,可以正常显示页面,如果用户没有登录,则自动跳转到登录页面进行登录,登录后才可以进行查看
钩子函数主要分为两大类:直接应用在APP上的、应用到蓝图上的
应用在APP上
before_first_request --- 第一次请求时运行
before_request --- 每次请求时运行
after_request --- 每次请求运行后执行
teardown_request --- 最后一次请求时运行
应用到蓝图上
before_app_first_request --- 在app启动时只使用一次
before_app_request --- 每次调用蓝图都会使用
after_app_request --- 在每次蓝图调用完,返回response的时候调用
teardown_app_request --- app运行完之后调用一次
example
# 要求登录后才能访问的url
required_login_list = ['/users/center',
'/users/change',
'/users/publish',
'/users/upload_photo',
'/users/photo_del',
'/articles/add_comment',
'/users/aboutme',
'/users/showabout']
# 在app启动时只使用一次
@users_bp.before_app_first_request
def first_request():
print('before_app_first_request')
if request.path in required_login_list:
print("需要验证用户的登录情况!")
# 每次调用蓝图都会使用
@users_bp.before_app_request
def before_request1():
print('before_app_request',request.path)
if request.path in required_login_list:
id = session.get('uid')
print('------>',id)
if not id:
return render_template('user/login.html')
else:
user = User.query.get(id)
# g对象 本次请求的对象
g.user = user
print(g.user.username)
# 在蓝图调用完,返回response的时候调用
@users_bp.after_app_request
def after_request_test(response):
print('after_app_request')
response.set_cookie('a','bbbb',max_age=19)
return response
# app运行完之后调用一次
@users_bp.teardown_app_request
def teardown_request_test(response):
print('teardown_app_request')
表单:
<form action="提交的路径" method="post" enctype="multipart/form-data" class="form-inline ">
<input type="file" name="photo" class="from-control">
<input type="submit" value="上传相册" class="btn btn-default">
form>
view视图:
# photo是FileStorage
photo = request.files.get('photo')
# 属性和方法:FileStorage---》fs
fs.filename
# path上传的路径:os.path.join(UPLOAD_PIR,filename)
fs.save(path)
# 将上传的内容改成二进制方式
fs.read()
本地资源优先或者是空间有限 https://developer.qiniu.com/kodo/1242/python ------> 参照python sdk
Example:
user/view.py 上传照片
@users_bp.route('/upload_photo',methods=['GET','POST'])
def upload_photo():
# 获取上传的内容
photo = request.files.get('photo')
ret,info = upload_qiniu(photo)
if info.status_code == 200:
photo = Photo()
photo.photo_name = ret['key']
photo.user_id = g.user.id
db.session.add(photo)
db.session.commit()
return '上传成功!'
else:
return '上传失败!'
util.py 使用qinniu存储
# 上传本地文件
def upload_qiniu(filestorage):
# 初始化Auth状态
q = Auth(access_key, secret_key)
# 初始化BucketManager
bucket = BucketManager(q)
# 获取文件名
filename = filestorage.filename
# 重新拼接成一个不重复的文件名
ran = random.randint(1,1000)
suffix = filename.rsplit('.')[-1]
key = filename.rsplit('.')[0]+ '_' +str(ran) + '.' +suffix
#生成上传 Token,可以指定过期时间等
token = q.upload_token(bucket_name, key, 3600)
#要上传文件的本地路径
# localfile = os.path.join(Config.UPLOAD_ICON_DIR,phone_name)
ret, info = put_file(token, key, filestorage.read(), version='v2')
# print(info)
assert ret['key'] == key
assert ret['hash'] == etag(filestorage.read())
return ret,info
# 返回所有记录
print(pagination.items)
# 返回当前的页码数
print(pagination.page)
# 返回当前页的前一个页码数
print(pagination.prev_num)
# 返回当前页的后一个页码数
print(pagination.next_num)
# 判断是否还有下一页
print(pagination.has_next)
# 判断是否有上一页
print(pagination.has_prev)
# 总共有几页
print(pagination.pages)
# 总的记录条数
print(pagination.total)
可参考另一篇文章:https://blog.csdn.net/weixin_42724501/article/details/121012689
1、使用app自带 默认输出到终端控制台
app.logger.info('这是一个info测试')
app.logger.debug('这是一个debug测试')
app.logger.warning('这是一个warning测试')
app.logger.error('这个是一个error测试')
2、通过logging进行创建
import logging
# 默认flask的名字叫:app
logger = logging.getLogger('name')
logger = logging.getLogger('app')
# 保存到文件
logging.basicConfig(filename='log.txt',filemode='a',level=logging.WARNING,
format='%(asctime)s - %(name)s - %(levelname)s -%(message)s')
'''
方法二:
logger.setLevel(level=logging.WARNING)
handler = logging.FileHandler("log2.txt")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
方法三:
使用logger.info('message')
'''
@users_bp.route('/logger')
def logger_test():
logger.warning("首页的警告!!!!!!")
# 需要打印到日志文件中,则:logging.getLogger('app')中必须是app
app.logger.warning('首页警告2!!!!!')
return render_template('')
redis的详细使用请查看官网,这里只简单地介绍在flask中的使用
1、导入相关package
pip install redis
pip install flask-caching
2、exts->init.py配置
cache = Cache()
3、init.py中初始化:
config = {
'CACH_TYPE': 'REDIS',
'CACHE_REDIS_HOST': '127.0.0.1',
'CACHE_REDIS_PORT': 6379
# 'CACHE_REDIS_PASSWORD':'',
# 'CACHE_REDIS_DB':''
}
def create_app():
cache.init_app(app = app,config = config)
4、使用
视图函数缓存
@users_bp.route('/')
@cache.cached(timeout=50) #过期时间
def index():
pass
缓存键值对
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()
redis基本语法
五种数据类型:string、hash、list、set、zset(sorted:有序集合)
string: SET [key] [value] 设置key-value
GET [key] 获取key相对应的value值
hash: 一个键值(key-value)对集合 一个string类型的field和value的映射表 适用于存储对象
HMSET [key] field1 [value1] field2 [value2] 设置hash列表值
HGET [KEY] field1 获取hash列表值
通用命令:
EXISTS [KEY] 检查指定key是否存在
DEL [KEY] 删除hash键值
keys pattern 查找所有符合给定模式的key
MOVE key db 将当前数据库的key移动到给定的数据库db当中
PERSIST key 移除key的国企时间,key将持久保持
PTTL key 以毫秒为单位返回key的剩余过期时间
EXPIRE key [seconds] 为给定的key设置过期时间,以秒计
EXPIREAT key [timestamp] 设置过期时间,时间戳
PEXPIRE key [milliseconds] 设置过期时间,以毫秒计
用于对提交的表单数据做格式验证,flask-wtf集成了wtform, csrf的保护和文件上传功能,以及图形验证码
使用
1、安装
pip install Flask-WTF
# 全局使用csrf保护
csrf = CSRFProtect(app=app)
# 必须需要设置SECRET_KEY这个配置项
app.conf['SECRET_KEY'] = 'fgfhdgfhh32'
2、定义form.py
# 在文件中添加
class UseForm(FlaskForm):
name = StringField('name',validators=[DataRequired()])
各种Field类型
类型 | |
---|---|
字符串 | StringField |
密码 | PasswordField |
整型 | IntegerField |
小数 | DecimalField |
浮点 | FloatField |
布尔 | BooleanField |
单选 | RedioField |
多选 | SelectField |
日期 | DatetimeField |
各种验证
validators | |
---|---|
数据校验 | DataRequired |
匹配校验 | EqualTo |
IP校验 | IPAddress |
长度校验 | Length |
数据范围校验 | NumberRange |
URL校验 | URL |
邮件格式校验 | |
正则校验 | Regexp |
3、html中设置
<form action="" method="post">
{{ uform.csrf_token }}
<p>{{ uform.name }}{% if uform.name.errors %}{{ uform.name.errors }}{% endif %}p>
<p>{{ uform.password }}{% if uform.password.errors %}{{ uform.password.errors }}{% endif %}p>
<p><input type="submit" value="提交"> p>
form>
4、views中提交验证
@users_bp.route('/testForm',methods=['GET','POST'])
def testForm():
uform = UserForm()
if uform.validate_on_submit():
print(uform.name)
print(uform.password)
return '提交成功!'
return render_template('formtest.html',uform)
5、文件上传
# 上传使用的是FileField,如果需要指定上传文件的类型需要使用:FileAllowed
class UserForm(FlaskForm):
icon = FileField(label="用户头像",Validators=[FileRequired(),FileAllowed(['jpg','png','gift'],message="必须是图片文件格式")])
模板中的使用同其他类型的字段,但是必须在form上面
enctype="multipart/form-data"
视图函数中如果验证成功,通过
# icon是FileStorage类型
icon = uform.icon.data
filename = secure_filename(icon.filename)
icon.save(os.path.join(UPLOAD_DIR,filename))
6、验证码(依赖谷歌传送验证码)
必须在setting.py配置
# secret_key加密码
SECRET_KEY = 'JIAMIMA'
RECAPTCHA_PUBLIC_KEY = "SHAJDGBCYSDCUDUSAD"
RECAPTCHA_PRIVATE_KEY = "AUERHDNSBDSDSADSLADDQ"
RECAPTCHA_PARAMETERS = {'hl':'zh','render':'explicit'}
RECAPTCHA_DATA_ATTRS = {'theme':'dark'}
form.py中
class UserForm(FlaskForm):
recaptcha = RecaptchaField(label="验证码")
7、验证码(自己绘制验证码)
pip install pillow
form.py
class UserForm(FlaskForm):
recaptchabyself = StringField(label='验证码')
def validate_recaptchabyself(self,data):
input_code = data.data
code = session.get('valid')
if input_code.lower() != code.lower():
raise ValidationError("验证码错误!")
工具类
# 获取随机颜色
def get_random_color(length):
return (random.randint(0,255),random.ranint(0,255),random.randint(0,255))
# 生成图片
def generate_image(length):
s = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRTSUVWXYZ1234567890'
size = (130,50)
# 创建画布
im = Image.new('RGB',size,color=get_random_color())
# 创建字体
ImageFont.truetype('font/segoerp.ttf',size=35)
# 创建ImageDraw对象
draw = ImageDraw.Draw(im)
code = ''
# 绘制验证码
for i in random(length):
c = random.choice(s)
code += c
# draw.text(坐标位置,绘制谁,用什么颜色,字体)
draw.text((5+ random.randint(4,7) + 25 * i, random.randint(1,4)),
text=c,fill=get_random_color(),
font=font)
# 绘制干扰线
for i in range(8):
x1 = random.randomint(0,130)
y1 = random.randomint(0,50/2)
x2 = random.randomint(0, 130)
y2 = random.randomint(50/2, 130)
draw.line(((x1,y1),(x2,y2)),fill=get_random_color())
im = im.filter(ImageFilter.EDGE_ENHANCE)
return im, code
视图
@users_bp.route('/testForm',methods=['GET','POST'])
def testForm():
uform = UserForm()
if uform.validate_on_submit():
print(uform.name)
print(uform.password)
name = uform.name.data
password = uform.password.data
phone = uform.phone.data
icon = uform.icon.data
filename = secure_filename(icon.filename)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
STATIC_DIR = os.path.join(BASE_DIR,'static')
UPLOAD_DIR = os.path.join(STATIC_DIR,'upload')
icon.save(os.path.join(UPLOAD_DIR,filename))
return '提交成功!'
return render_template('formtest.html',uform)
@users_bp.route('/image')
def get_image():
im,code = generate_image()
# 将image对象转成二进制
buffer = BytesIO()
im.save(buffer,"JPEG")
buf_bytes = buffer.getvalue()
# 保存到session中
session['valid'] = code
response = make_response(buf_bytes)
response.headers['Content-Type'] = 'image/jpg'
return response
html
<p>{{ uform.recaptchabyself.label }}:{{ uform.recaptchabyself }}<img src="{{ url_for('users.get_image') }}" alt="" id="img">
<p>{% if uform.recaptcha.errors %}{{ uform.recaptcha.errors.0 }}{% endif %}p>
8、消息闪现
a、在一个请求结束的时候添加flash
flash('登录成功!','info')
flash(username,'warning')
flash('hahha', 'error')
b、在当前请求中渲染获取或者仅仅下一个请求中可以获取
获取闪现内容:
1、 with_categories=True为获取多个消息(元组)
get_flashed_messages(with_categories=[True/False])
2、 有针对性地获取可闪现消息
get_flashed_messages(category_filter=["error"])