一、安装1.安装ssh新版本安装位 apt/apt-get老版本为 apt-get安装以后 ifconfig查看当前的ip地址如果网络不在同一个ip段 将网络模式设置为桥接2.安装pip3sudo apt install python3-pip在要安装项目的目录创建虚拟环境virtualenv venvsource activate # 开启虚拟开发环境模式退出 deactivatepip3 install uwsgi # 安装uwsgi二、配置uwsgi配置文件支持很多格式，我采用.ini格式，命名为uconfig.ini具体内容如下：socket指出了一个套接字，相当于为外界留出一个uwsgi服务器的接口。[uwsgi] # 外部访问地址，可以指定多种协议，现在用http便于调试，之后用socket # socket = 0.0.0.0:8000 # uwsgi的监听端口 # 指向项目目录 chdir = /home/xlg/blog/ # flask启动程序文件 wsgi-file = manage.py # flask在manage.py文件中的app名 callable = app plugins = python# 这行一定要加上，不然请求时会出现-- unavailable modifier requested: 0 --错误提示 # 处理器数 processes = 1 # 线程数 threads = 2 #状态检测地址 #stats = 127.0.0.1:9191socket和http的差别。从概念上来说，socket本身不是协议而是一种具体的TCP/IP实现方式，而HTTP是一种协议且基于TCP/IP。具体到这个配置这里来，如果我只配了socket = 127.0.0.1:5051的话，通过浏览器或者其他HTTP手段是无法成功访问的。而在uwsgi这边的日志里会提示请求包的长度超过了最大固定长度。另一方面，如果配置的是http = 127.0.0.1:5051的话，那么就可以直接通过一般的http手段来访问到目标。但这会引起nginx无法正常工作。正确的做法应该是，如果有nginx在uwsgi之前作为代理的话应该配socket，而如果想让请求直接甩给uwsgi的话那么就要配httppythonpath指出了项目的目录，module指出了项目启动脚本的名字而紧接着的wsgi-file指出了真正的脚本的文件名。callable指出的是具体执行.run方法的那个实体的名字，一般而言都是app=Flask(__name__)的所以这里是app。processes和threads指出了启动uwsgi服务器之后，服务器会打开几个并行的进程，每个进程会开几条线程来等待处理请求，显然这个数字应该合理，太小会使得处理性能不好而太大则会给服务器本身带来太大负担。daemonize项的出现表示把uwsgi服务器作为后台进程启动，项的值指向一个文件表明后台中的所有输出都重定向到这个日志中去。daemonize = /home/wyz/flask/server.log三、安装 nginxsudo apt-get install nginxNginx是轻量级、性能强、占用资源少，能很好的处理高并发的反向代理软件。Ubuntu 上配置 Nginx 也是很简单，不要去改动默认的 nginx.conf 只需要将/etc/nginx/sites-available/default文件替换掉就可以了。 新建一个 default 文件:server{ listen 80; # 服务器监听端口 server_name 10.0.121.116; # 这里写你的域名或者公网IP location / { uwsgi_pass 127.0.0.1:8000; # 转发端口，需要和uwsgi配置当中的监听端口一致 include uwsgi_params; # 导入uwsgi配置 #uwsgi_param UWSGI_PYTHON /home/自己创建的目录/venv; # Python解释器所在的路径（这里为虚拟环境） uwsgi_param UWSGI_PYTHON /usr/bin/python3; uwsgi_param UWSGI_CHDIR /home/xlg/blog/;# # 自己创建的目录 项目根目录 uwsgi_param UWSGI_SCRIPT manage:app; # 指定启动程序 #比如你测试用test.py文件，文件中app = Flask(name)，那么这里就填 test：app } }服务启动sudo service nginx startsudo service nginx stopsudo service nginx restart安装MySQL数据库sudo apt-get install mysql-server指定配置文件，后台运行 uwsgi， 这时再刷新一下之前打开的页面，就可以看到应用正常运行了。uwsgi uconfig.ini 访问地址192.168.100.136pip3 -V 查看是哪个python解释器的pip3 list 查看安装了哪些包pip3 freeze 安装的模块和版本号pip3 freeze>requirements.txt 导出所有的安装过的模块信息到一个文件中pip3 install -r requirements.txt 从导出的模块信息文件中安装所有的模块包含的模块：flaskpymysqlflask-wtfflask-loginflask-mailflask-scriptflask-bootstrapflask-cacheflask-sqlalchemypillowflask-migrateflask-momentflask-uploads日志分类Nginx日志主要分为两种：访问日志和错误日志。日志开关在Nginx配置文件（/etc/nginx/nginx.conf）中设置，两种日志都可以选择性关闭，默认都是打开的。access_log /var/log/nginx/access.log;error_log /var/log/nginx/error.log;
一、Model对数据的操作3种办法(1) 最原始的 提交 和 回滚#最原始的添加 提交或者回滚 每次都需要 @test.route('/insert_one/') def insert_one(): try: # u = User(username='zhangshang',sex=False,age=18) u = User('lisi',True,20) db.session.add(u) db.session.commit() except: db.session.rollback() return 'insert_one'注意：每次都需要提交和回滚(2) 在settings.py进行配置SQLALCHEMY_COMMIT_ON_TEARDOWN = True每次添加或者保存，修改，删除数据 都不需要commit() 或者 rollback()实例app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True #设置自动提交 #settings.py已经设置为数据自动提交 @test.route('/auto_insert_one/') def auto_insert_one(): u = User('王五',False,30) db.session.add(u) return 'insert_one'(3) 自定义增删改的基类 进行封装user.py中base类的书写class Base: #添加一条数据 def save(self): try: db.session.add(self) db.session.commit() except: db.session.rollback() #添加多条数据 @staticmethod def save_all(*args): try: db.session.add_all(args) db.session.commit() except: db.session.rollback() #自定义删除基类 def delete(self): try: db.session.delete(self) db.session.commit() except: db.session.rollback()user.py中base类的使用class User(Base,db.Model): __tablename__ = 'user' id = db.Column(db.Integer,primary_key=True) username = db.Column(db.String(12),index=True) sex = db.Column(db.Boolean,default=True) age = db.Column(db.Integer,default=20) icon = db.Column(db.String(40),default='default.jpg') def __init__(self,username,sex=True,age=18,icon='default.jpg'): self.username = username self.sex = sex self.age = age self.icon = icon蓝本文件中的使用#使用自定义的base类 进行添加 自带提交和回滚 @test.route('/self_insert_one/') def self_insert_one(): u = User('兆六',False,20) u.save() return 'insert_one' #添加多条数据 @test.route('/self_insert_many/') def self_insert_many(): sex = [True,False] u1 = User(random.randrange(1000,10000),sex[random.randint(0,1)],random.randint(1,100)) u2 = User(random.randrange(1000,10000),sex[random.randint(0,1)],random.randint(1,100)) User.save_all(u1,u2) return '添加多条' #自定义删除方法的使用 @test.route('/delete/') def delete(): u = User.query.get(2) u.delete() return '删除' #自定义修改方法的使用 @test.route('/update/') def update(): u = User.query.get(2) u.username = '张三' u.save() return '修改'二、数据的操作查询集： 数据查询的集合分类：原始查询集就是没有经过过滤器筛选的集合数据查询集经过过滤器筛选 最终拿到你的数据的集合过滤器 链式调用如： a().b().c()(1) all() 以列表的形式 得到数据查询集使用@test.route('/all/') def show(): dataList = User.query.all() print(dataList) #[<user 1>,<user 2>...] return render_template('show.html',data=dataList)(2) filter() 过滤出你要得到的数据如果没有参数 默认查询所有User.query.filter(User.username=='张',User.sex==True) #查询name为张 并且sex为True@test.route('/filter/') def filter(): data = User.query.filter() #查询所有 data = User.query.filter(User.sex==True) #查询性别为True data = User.query.filter(User.sex==True,User.age==43) #查询性别为True and age为43 print(data) return render_template('show.html',data=data)(3) filter_by() 只支持单条件查询 = #单条件查询 @test.route('/filter_by/') def filter_by(): data = User.query.filter_by() #查询所有 data = User.query.filter_by(sex=True) #查询sex为True的数据 data = User.query.filter_by(sex=True,age=43) #查询sex为True 并且 age为43的数据 print(data) return render_template('show.html', data=data)注意：因为 filyter_by的参数为**kwargs 所以只能使用关键字参数(4) offset(num) 偏移量 偏移几条数据#offset 偏移量 @test.route('/offset/<int:num>/') def offset(num): data = User.query.offset(num) return render_template('show.html', data=data)(5) limit(num) 取出num条数据#limit 偏移量 @test.route('/limit/<int:num>/') def limit(num): data = User.query.limit(num) return render_template('show.html', data=data)(6) offset().limit() 组合使用@test.route('/offsetlimit/<int:num>/') def offsetlimit(num): data = User.query.offset(num).limit(num) return render_template('show.html', data=data)(7) order_by() 排序 升序/降序升序 类名.属性名降序 -类名.属性名#order_by 排序 @test.route('/order_by/') def order_by(): # data = User.query.order_by(User.id) # data = User.query.order_by(-User.id) # data = User.query.filter_by(sex=True).order_by(-User.id) data = User.query.order_by(-User.age).limit(1) return render_template('show.html', data=data)(8) first() 取出第一条数据#取出第一条数据 @test.route('/first/') def first(): data = User.query.order_by(-User.id).first() #== data = User.query.all()[0] print(data) return 'first'(9) get() 根据id查询@test.route('/get/') def myGet(): data = User.query.get(20) print(data) return 'get'注意：如果查询不到 则为None 否则返回对象(10) contains() 包含关系就是模糊查询@test.route('/contains/') def contains(): data = User.query.filter(User.username.contains('1')) return render_template('show.html', data=data)(11) like 模糊查询@test.route('/like/') def like(): data = User.query.filter(User.username.like('%1%')) #包含1 data = User.query.filter(User.username.like('%1')) #1结尾 data = User.query.filter(User.username.like('1%')) #1开头 return render_template('show.html', data=data)(12) startswith endswith 以...开头 以...结尾@test.route('/startend/') def startend(): data = User.query.filter(User.username.startswith('张')) #以张作为开头 data = User.query.filter(User.username.endswith('六')) #以六作为结尾 return render_template('show.html', data=data)(13) 比较运算符__gt__ 大于__ge__ 大于等于__lt__ 小于__le__ 小于等于><>=<===!=data = User.query.filter(User.sex!=True) #查询所有 data = User.query.filter(User.sex!=True,User.age>=30) #查询所有 data = User.query.filter(User.age.__gt__(30)) #查询所有 data = User.query.filter(User.age.__ge__(30)) #查询所有 data = User.query.filter(User.age.__lt__(30)) #查询所有 data = User.query.filter(User.age.__le__(30)) #查询所有 data = User.query.filter(User.age.__gt__(30),User.age.__lt__(70)) #查询所有(14) in_ 和 not in@test.route('/in/') def myIn(): data = User.query.filter(User.id.in_([1,2,3,4])) #in data = User.query.filter(~User.id.in_([1,2,3,4])) #not in return render_template('show.html', data=data)(15) 查询为null的数据@test.route('/null/') def null(): data = User.query.filter(User.username.is_(None)) data = User.query.filter(User.username == None) data = User.query.filter(~User.username.isnot(None))(16) 查询不为null的@test.route('/notnull/') def notnull(): data = User.query.filter(User.username!=None) data = User.query.filter(~User.username.is_(None)) data = User.query.filter(User.username.isnot(None))(17) 逻辑与查询 and_多个条件 逗号隔开from sqlalchemy import and_实例from sqlalchemy import and_ @test.route('/and/') def myAnd(): #查询性别为True 并且名字包含1的数据 data = User.query.filter(User.username.contains('1'),User.sex==True) data = User.query.filter(and_(User.username.contains('1'),User.sex==True)) data = User.query.filter(User.username.contains('1')).filter(User.sex==True) return render_template('show.html', data=data)(18) 逻辑或 or_from sqlalchemy import or_@test.route('/or/') def myOr(): #or的使用 data = User.query.filter(or_(User.username.contains('1')，User.sex==True)) #and 和 or使用 data = User.query.filter(or_(User.username.contains('1'),User.sex==True),User.age>=60) return render_template('show.html', data=data)(19) 逻辑非 not_from sqlalchemy import not_@test.route('/not/') def myNot(): # data = User.query.filter(not_(User.username.contains('1'))) # data = User.query.filter(~(User.username.contains('1'),User.sex==True)) #错误写法 data = User.query.filter(~(User.username.contains('1'))) return render_template('show.html', data=data)注意：not_和～都只能给一个条件进行取反(20) count() 统计个数@test.route('/count/') def count(): data = User.query.filter().count() print(data) return '{}'.format(data)三、文件的迁移(falsk-migrate)(1) 安装pip install flask-migrate(2) 创建迁移对象from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate db = SQLAlchemy(app) #ORM实例化 migrate = Migrate(app,db) #实例化迁移类 from flask_migrate import MigrateCommand manager.add_command('db',MigrateCommand) #将迁移的命令添加到终端运行解析器 通过命令进行迁移(3) 创建迁移目录python3 manage.py db init (4) 生成迁移文件python3 manage.py db migrate(5) 执行迁移文件python3 manage.py db upgrade注意：如果生成迁移文件的时候报错了,将migrations目录中的版本号文件删除掉,将数据库中的迁移文件版本号的表也删除掉。如果在生成迁移文件的时候告诉你，当前模型没有任何的改变，那么可能是没有检测到模型的存在，此时去导入一下再重新生成迁移文件（这个问题不是只有flask有 在django也可能遇到）。
一、flash消息的显示概述： 当用户状态发生改变等等的处理时， 需要给用户显示出提示信息 flash就是实现这个功能的导入： from flask import flash,get_flashed_messagesflash 存数据get_flashed_messages() 获取flash存储的数据实例manage.py#ajax处理登录的视图 @app.route('/do_login/',methods=['POST']) def dologin(): # print(request.args.get('username')) username = request.form.get('username') userpass = request.form.get('userpass') if username == 'zhangsan': if userpass == '123456': flash("登录成功") # flash("登录成功2") # flash("登录成功3") # print(get_flashed_messages()) data = {'code':200} else: data = {'code':400} else: data = {'code':404} return jsonify(data)base.html{% for message in get_flashed_messages() %} × Warning! {{ message }} SS {% endfor %}二、flask-moment 时间显示的扩展库概述： 专门负责时间的显示 可以显示当前时间 可以计算时间的差值附：用法导入：from flask_moment import Momentmanage.pyfrom flask_moment import Moment #时间模块 from datetime import datetime,timedelta #导入时间模块 moment = Moment(app) manager = Manager(app) @app.route('/') def index(): nowtime = datetime.utcnow()+timedelta(seconds=-60) #前60秒的时间 return render_template('index.html',nowtime=nowtime)index.html 首页 {{ nowtime }} {{ moment(nowtime).format('L') }} {{ moment(nowtime).format('LL') }} {{ moment(nowtime).format('LLL') }} {{ moment(nowtime).format('LLLL') }} {{ moment(nowtime).format('YYYY-MM-DD') }} {{ moment(nowtime).fromNow() }} {% endblock %} {% block scripts %} {{ super() }} {{ moment.include_moment() }} #导入moment显示的js代码 {{ moment.locale('zh-CN') }} #格式化中国的时间 {% endblock %}三、文件上传1.原生文件上传(1)简单上传manage.py@ys.route('/form/',methods=['GET','POST']) def form(): # if request.method == 'POST' and request.files.get('photo'): if request.method == 'POST' and 'photo' in request.files: # print(request.files) #获取传递过来的文件 file = request.files.get('photo') #获取文件名称 filename = file.filename #存储上传的路径和名字 file.save(os.path.join(os.getcwd(),'static/upload/'+filename)) return 'ok' return render_template('ysfile/form.html')form.html 文件上传 附：python生成随机图形验证码(2)添加条件判断的上传manage.pyfrom flask import Blueprint,render_template,request,current_app import string,random,os #pip install pillow 处理图片 from PIL import Image ys = Blueprint('ys',__name__) #设置允许上传的文件类型 ALLOWED_FILE = ['jpg','jpeg','gif','png'] """ 文件上传的判断条件 有哪些？ 1.类型 2.大小 3.处理文件名的生成 获取后缀的方法？ 4.上传 5.获取到名称 在模板中展示出来 """ #生成随机文件名字的函数 def random_name(suffix,length=64): #生成大小写字母和数字的字符串 Str = string.ascii_letters+string.digits #循环生成64位长度数字和字母的字符串的随机数+后缀 return ''.join(random.choice(Str) for i in range(length))+'.'+suffix #图片缩放函数 def img_zoom(path,width=200,height=200,prefix='s_'): img = Image.open(path) print(img.size) # 获取图片的宽 高 img.thumbnail((width, height)) # 重新设计尺寸 pathSplit = os.path.split(path) # 将路径拆分成 路径和文件名 newPath = os.path.join(pathSplit[0],prefix+pathSplit[1]) #将前缀进行拼接 img.save(newPath) # 保存图片 覆盖掉原来的图片 @ys.route('/form/',methods=['GET','POST']) def form(): img_name = None if request.method == 'POST' and 'photo' in request.files: file = request.files.get('photo') #拿到上传文件 suffix = file.filename.split('.')[-1] #获取后缀 if suffix in ALLOWED_FILE: #判断是否允许的文件类型 #确保名字唯一性 while True: newName = random_name(suffix) #生成随机名字 path = os.path.join(current_app.config['UPLOADED_FOLDER'], newName) if not os.path.exists(path): break #保存 file.save(path) #将名字传递给模板进行展示 #进行图片的缩放 img_zoom(path) img_name = newName return render_template('ysfile/form.html',img_name=img_name)form.html Title {# 当文件上传失败 查看几点错误原因 methods是否为post input type=file 是否有name名称 是否更改了enctype的值 #} {% if img_name %} {# #} {% endif %} 文件上传 2.使用flask-uploads进行文件上传概述： 在文件上传时，提供了文件过滤和校验的功能导入： from flask_uploads import IMAGES,UploadSet,configure_uploads,patch_request_classmanage.pyfrom flask import Flask,request,render_template from flask_script import Manager from flask_bootstrap import Bootstrap import os from flask_uploads import IMAGES,UploadSet,configure_uploads,patch_request_class app = Flask(__name__) #设置上传文件的大小 配置 写死的 app.config['MAX_CONTENT_LENGTH'] = 1024*1024*64 #64兆 app.config['UPLOADED_FOLDER'] = os.path.join(os.getcwd(),'static/upload') #flask-uploads上传的时候 自动查找存储文件的目录 app.config['UPLOADED_PHOTOS_DEST'] = os.path.join(os.getcwd(),'static/upload') bootstrap = Bootstrap(app) file = UploadSet('photos',IMAGES) #配置文件上传 configure_uploads(app,file) #将app和配置关联到一起 patch_request_class(app,size=None) #使上传文件大小从 app.config['MAX_CONTENT_LENGTH'] 进行获取 manager = Manager(app) @app.route('/') def index(): return 'index' @app.route('/wtfrom/',methods=['GET','POST']) def wtfrom(): img_name = None if request.method == 'POST' and 'photo' in request.files: filename = file.save(request.files.get('photo')) print(filename) img_name = file.url(filename=filename) print(img_name) return render_template('ysfile/form.html',img_name=img_name) from yuanshengfile import ys # from flask_wtf_form import fwf app.register_blueprint(ys) #注册原生文件上传的蓝本 # app.register_blueprint(fwf) #注册flask-wtf文件上传的蓝本 if __name__ == '__main__': manager.run()form.html Title {# 当文件上传失败 查看几点错误原因 methods是否为post input type=file 是否有name名称 是否更改了enctype的值 #} {% if img_name %} {# #} {% endif %} 文件上传 四、flask-wtf flask-uploads flask-bootstrap 表单manage.pyfrom flask import Flask,render_template from flask_bootstrap import Bootstrap from flask_script import Manager from flask_wtf import FlaskForm from flask_wtf.file import FileField,FileRequired,FileAllowed #导入自定义文件上传的表单 所需要的字段和验证器 from wtforms import SubmitField from flask_uploads import UploadSet,IMAGES,patch_request_class,configure_uploads #文件上传扩展库 所需要的 import os,random,string from PIL import Image app = Flask(__name__) app.config['SECRET_KEY'] = 'flask-wtf' #密钥 值随便 app.config['BOOTSTRAP_SERVE_LOCAL'] = True #加载本地bootstrap样式 app.config['MAX_CONTENT_LENGTH'] = 1024*1024*64 #设置上传文件大小 app.config['UPLOADED_PHOTOS_DEST'] = os.path.join(os.getcwd(),'static/upload') #设置上传文件的保存目录 file = UploadSet('photos',IMAGES) #上传文件类型允许的设置 configure_uploads(app,file) #将配置和flask绑定 patch_request_class(app,size=None) #设置上传大小 获取MAX——CONTENT——LENGTH的大小 bootstrap = Bootstrap(app) manager = Manager(app) #配置上传表单 class FileUpload(FlaskForm): photos = FileField('上传头像',validators=[FileRequired(message='文件不能为空'),FileAllowed(file,message='只能上传图片')]) submit = SubmitField('上传') #随机名字 def random_name(suffix,length=32): Str = string.ascii_letters+string.digits return ''.join(random.choice(Str) for i in range(length))+'.'+suffix #图片缩放函数 def img_zoom(path,width=200,height=200,prefix='s_'): img = Image.open(path) print(img.size) # 获取图片的宽 高 img.thumbnail((width, height)) # 重新设计尺寸 pathSplit = os.path.split(path) # 将路径拆分成 路径和文件名 newPath = os.path.join(pathSplit[0],prefix+pathSplit[1]) #将前缀进行拼接 img.save(newPath) # 保存图片 覆盖掉原来的图片 @app.route('/form/',methods=['GET','POST']) def upload(): img_url = None form = FileUpload() if form.validate_on_submit(): photo = form.photos.data #request.files.get('photos') suffix = photo.filename.split('.')[-1] #取出后缀 while True: #确保生成的名字唯一性 filename = random_name(suffix) path = os.path.join(app.config['UPLOADED_PHOTOS_DEST'],filename) if not os.path.exists(path): break #保存图片 并使用生成的随机名字 file.save(photo,name=filename) #进行图片的缩放 img_zoom(path) #进行图片的缩放 img_zoom(path,300,300,'m_') #获取上传图片的url地址 img_url = file.url(filename) return render_template('form.html',form=form,img_url=img_url) if __name__ == '__main__': manager.run()form.html{% extends 'bootstrap/base.html' %} {% block title %} 最终版 文件上传 {% endblock %} {% from 'bootstrap/wtf.html' import quick_form %} {% block content %} {% if img_url %} {% endif %} 文件上传 {{ quick_form(form) }} {% endblock %}五、设置临时的环境变量ubuntu：设置 export 名=值获取 echo $名windwos：设置 set 名=值获取 set名六、发送邮件(flask-mail)manage.pyfrom flask import Flask,render_template from flask_bootstrap import Bootstrap from flask_script import Manager from flask_mail import Mail,Message import os,time app = Flask(__name__) app.config['MAIL_SERVER'] = os.environ.get('MAIL_SERVER','smtp.1000phone.com') app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME','xialigang@1000phone.com') app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD','123456') bootstrap = Bootstrap(app) manager = Manager(app) #发送邮件对象 mail = Mail(app) @app.route('/send_mail/') def send_mail(): msg = Message(subject='账户激活',recipients=['948807313@qq.com'],sender=app.config['MAIL_USERNAME']) msg.html = render_template('send_mail.html') mail.send(message=msg) # time.sleep(2) return '发送邮件' if __name__ == '__main__': manager.run()异步发送 开启线程from flask import Flask,render_template from flask_bootstrap import Bootstrap from flask_script import Manager from flask_mail import Mail,Message import os,time from threading import Thread #导入线程模块 app = Flask(__name__) app.config['MAIL_SERVER'] = os.environ.get('MAIL_SERVER','smtp.1000phone.com') app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME','xialigang@1000phone.com') app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD','123456') bootstrap = Bootstrap(app) manager = Manager(app) #发送邮件对象 mail = Mail(app) def send_mail_async(msg): #管理程序上下文 with app.app_context(): mail.send(message=msg) @app.route('/send_mail/') def send_mail(): msg = Message(subject='账户激活',recipients=['948807313@qq.com'],sender=app.config['MAIL_USERNAME']) msg.html = render_template('send_mail.html') thr = Thread(target=send_mail_async,args=(msg,)) thr.start() #开启线程 # time.sleep(2) return '发送邮件' if __name__ == '__main__': manager.run()
一、Modelflask作为一款MVT的框架，也具有ORM模型的操作，通过扩展库(flask-sqlalchemy)来实现。随着项目越来越大，采用原生sql的话，就会出现大量的原生sql语句。ORM模型的好处：SQL语句重复使用率低，越复杂的sql语句，语句就越长，会出现很多相似的sql语句很多sql语句都是根据业务逻辑拼接出来的，如果数据库发生了修改，那么这些逻辑就需要重写 这样就会漏掉某些sql的更改原生SQL容易漏掉，对sql语句安全性的考虑工作原理：ORM 是对象关系的映射，我们可以通过类的方式去操作数据库，不再使用原生的SQL语句了。ORM中的类映射数据库中的每个表，类中的每个属性映射成表中的每个字段。ORM最终还是会将开发者操作的增删改查的代码转换成sql语句，再去操作数据库。好处：可以使用简短的代码写出比较复杂的SQL语句可以减少重复sql语句的概率可移植性好，只需要更改settings.py的配置sqlite,mysql等二、原生SQL(1) 创建数据库 python1807create database if not exists python1807 character set utf8;(2) 安装pymysql模块pip install pymysql(3) 安装flask-sqlalchemypip install flask-sqlalchemy(4) 配置链接数据库的URI地址#配置链接数据库的URI地址 HOST = '127.0.0.1' USER = 'root' PASSWORD = '123456' PORT = '3306' NAME = 'python1807' DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(USER,PASSWORD,HOST,PORT,NAME)实例from sqlalchemy import create_engine #配置链接数据库的URI地址 HOST = '127.0.0.1' USER = 'root' PASSWORD = '123456' PORT = '3306' NAME = 'python1807' DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(USER,PASSWORD,HOST,PORT,NAME) #创建sqlalchemy操作数据库引擎 engine = create_engine(DB_URI) with engine.connect() as con: con.execute('drop table user ') con.execute('create table user(id int,username varchar(10))') con.execute('drop table user ') #在windows下 库，表名 不区分大小写 linux下 严格区分大小写三、flask-sqlalchemy Model(1) 导入from flask-sqlachemy import SQLAlchemy(2) 配置URI地址app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@127.0.0.1:3306/python1807' #配置链接数据库的地址四、model的字段 可选项(1) 字段类型类型名python类型说明Integerint整形 2(10）SmallIntegerint小整形BigerIntegerint长整形Floatfloat浮点形Stringstringvarchar类型 0-255Textstringtext 类型 0-65535BooleanboolTrue FalseDatedatetime.date日期Timedatetime.time时间DateTimedatetime.datetime日期和时间(2) 约束条件选项说明primary_key主键索引unique唯一索引index常规索引nullable是否为空 默认Truedefault默认值五、创建模型配置from flask import Flask from flask_script import Manager from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@127.0.0.1:3306/python1807' #配置链接数据库的地址 app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) manager = Manager(app)实例 创建user模型#创建模型 class User(db.Model): id = db.Column(db.Integer,primary_key=True) username = db.Column(db.String(20),index=True) sex = db.Column(db.Boolean,default=True) age = db.Column(db.Integer,default=20)创建表@app.route('/create_table/') def create_table(): db.create_all() return '创建表'删除表@app.route('/drop_table/') def drop_table(): db.drop_all() return '删除表'注意：创建表和删除表都是使用当前模型名创建模型的，表删除模型同名的所有的表六、数据添加 修改 删除 查询的操作(1) insert_one 添加一条数据#添加一条数据 @app.route('/insert_one/') def insert_one(): u = User(username='张三',sex=False,age=18) db.session.add(u) db.session.commit() return 'insert_one'(2) insert_many 添加多条数据#添加多条数据 @app.route('/insert_many/') def insert_many(): try: u1 = User(username='李四') u2 = User(username='王五') db.session.add_all([u1,u2]) db.session.commit() except: db.session.rollback() return 'insert_many'(3) 数据的查询@app.route('/select/') def select(): u = User.query.get(1) # print(u) print(u.username) print(u.sex) print(u.age) return '查询id值'(4) 数据的修改@app.route('/update/') def update(): u = User.query.get(1) u.username = '张三123' u.age = 30 db.session.add(u) db.session.commit() return '修改'(5) 数据的删除#删除 @app.route('/delete/') def delete(): u = User.query.get(1) db.session.delete(u) db.session.commit() return '删除'注意：ORM开启了事物处理 在执行增 删 改 的时候 要提交或者 回滚db.session.commit()db.session.rollback()七、拆分MVT目录层级project/ App/ templates/ 模板 common/ base.html static/ 静态 upload/ models/ 模型 __init__.py views/ 视图 __init__.py forms/ 表单 __init__.py extensions.py 加载扩展库 settings.py 配置文件 email.py 邮件 __init__.py 包的初始化 migrations/ #迁移目录 manage.py #启动
一、原生表单1. 添加模板文件login.html，内容如下： 用户名： 2. 视图函数，如下：# 登录 @app.route('/login/') def login(): return render_template('login.html') P # 校验 @app.route('/check/', methods=['POST']) def check(): print(request.form.get('username')) return 'Hello %s !' % request.form['username']3. 将登录页面和校验的路由合并，如下：@app.route('/login/', methods=['GET', 'POST']) def login(): if request.method == 'GET': return render_template('login.html') else: return 'Hello %s !' % request.form['username']一个路由处理时，表单的action属性不用书写，默认提交到当前路由。二、flask-wtf处理表单说明：flask-wtf是一个用于表单处理的扩展库，提供了CSRF、校验等功能，使用非常方便附：flask-wtf教程1.跨站请求伪造保护(csrf)附：浅谈CSRF攻击方式Flask-WTF 能保护所有表单免受跨站请求伪造(Cross-Site Request Forgery,CSRF)的攻击。恶意网站把请求发送到被攻击者已登录的其他网站时就会引发 CSRF 攻击。为了实现 CSRF 保护,Flask-WTF 需要程序设置一个密钥。Flask-WTF 使用这个密钥生成加密令牌,再用令牌验证请求中表单数据的真伪。#视图中 设置 Flask-WTF app = Flask(__name__) app.config['SECRET_KEY'] = 'hard to guess string'#字典可用来存储框架、扩展和程序本身的配置变量。 #为了增强安全性,密钥不应该直接写入代码,而要保存在环境变量中。 #模板中使用 {{ form.hidden_tag() }} #或 {{ form.csrf_token }}2.关闭csrf@app.route('/form/',methods=['post','get']) def newform(): form = NameForm(csrf_enabled = False)3.flask-wtf常见字段类型和验证器常见字段类型字段类型说明StringField普通文本字段SubmitField提交按钮PasswordField密码文本字段HiddenField隐藏文本字段TextAreaField多行文本字段DateField文本字段，datetime.date格式DateTimeField文本字段，datetime.datedatime格式IntegerField文本字段，值为整数FloatField文本字段，值为小数BooleanField复选框，值为True或FalseRadioField单选框SelectField下拉框FileField文件上传常见验证器类验证器说明DataRequired确保字段有值 必填项Email邮箱地址 验证邮箱IPAddressIP地址Length规定字符长度 有min和max两个值进行限制。NumberRange输入数值的范围EqualTo验证两个字段的一致性URL有效的URLRegexp正则验证info = TextAreaField('个人信息',render_kw={'style':'resize:none;'}) #render_kw 给当前的标签添加属性表单类每个 Web 表单都由一个继承自Form的类表示。这个类定义表单中的一组字段（表单的内容与类的字段是一一对应的）,每个字段都用对象（表单中的对象）表示。字段对象可附属一个或多个验证函数。4.定义表单类# 导入表单基类 from flask_wtf import FlaskForm # 导入字段类型 from wtforms import StringField, SubmitField # 导入验证器类 from wtforms.validators import DataRequired #必填字段 # CSRF需要使用 app.config['SECRET_KEY'] = '123456' # 定义表单类 class NameForm(FlaskForm): #name 为表单的name名称 name = StringField('用户名', validators=[DataRequired()]) submit = SubmitField('提交')5.添加视图函数：@app.route('/') def index(): # 创建表单对象 form = NameForm() # 分配到模板中进行渲染 return render_template('form.html', form=form)6.模板文件中渲染表单：Form 类有一个 hidden_tag 方法， 它在一个隐藏的 DIV 标签中渲染任何隐藏的字段，包括 CSRF 字段:{# 原生渲染 #} {# CSRF的隐藏字段 #} {{ form.hidden_tag() }} #或 {{ form.csrf_token }} {# name字段，可以指定id、class等属性，定制显示效果 style='color:red 设置文本颜色 #} {{ form.name.label() }}{{ form.name(id='xxx', class='yyy',style='color:red) }} {{ form.name.errors }} #错误提示 {# submit字段 #} {{ form.submit() }} 7.在视图中使用bootstrap的wtf快速渲染表单#使用bootstrap快速渲染 其它代码不变 from flask_bootstrap import Bootstrap app.config['BOOTSTRAP_SERVE_LOCAL'] = True #加载本地的js文件 bootstrap = Bootstrap(app) {% extends 'bootstrap/base.html' %} {% import 'bootstrap/wtf.html' as wtf %} {{ wtf.quick_form(form) }}模板中更改表单提交地址和表单排列样式wtf.quick_form(form,form_type='horizontal',action= url_for('login') )查看都有哪些参数可以查看 wtf.html 里面的 quick_form 宏{# bootstrap渲染 #} {% extends 'bootstrap/base.html' %} {# 导入渲染工具 #} {% import 'bootstrap/wtf.html' as wtf %} {# 渲染表单 调用宏方法 wtf.quick_form#} {% block content %} {{ wtf.quick_form(form,action=url_for('upload')) }} {% endblock %}8.表单校验@app.route('/', methods=['GET', 'POST']) def index(): # return '表单使用' name = None # 创建表单对象 form = NameForm() # 表单校验 #validate_on_submit 会便捷地检查该请求是否是一个 POST 请求以及是否有效。 if form.validate_on_submit(): name = form.name.data form.name.data = '' # 分配到模板中进行渲染 return render_template('form.html', form=form, name=name)9.自定义表单验证器自定义字段验证：就是写一个validate_字段名'的函数，如下：# 定义表单类 class NameForm(FlaskForm): name = StringField('用户名', validators=[DataRequired()]) submit = SubmitField('提交') #如果字段中也存在Length对象的验证 那么这俩个验证会重复叠加 #userName = StringField('用户名',validators=[DataRequired(),Length(3, 6, message='请输入正确的长度')]) # 自定义字段验证函数，格式是写一个'validate_字段名'的函数 def validate_name(self, field): if len(field.data) < 6: raise ValidationError('用户名长度不能少于6个字符')10.所有的字段和验证器的使用class Form(FlaskForm): hidde = HiddenField() username = StringField('用户名') userpass = PasswordField('密码') confirm = PasswordField('确认密码',validators=[EqualTo('userpass',message='密码和确认密码不一致')]) info = TextAreaField('个人信息',render_kw={'style':'resize:none;'}) birth = DateField('出生年月',format='%Y/%m/%d') birthday = DateTimeField('生日日期') age = IntegerField('age',validators=[NumberRange(min=6,max=99,message='年龄范围6~99')]) money = FloatField('money') rember = BooleanField('记住我') sex = RadioField('sex',choices=[('m','男'),('w','女')]) address = SelectField('address',choices=[('1001','北京'),('1002','上海'),('1003','深圳')]) photo = FileField('photo') email = StringField('email',validators=[Email(message='请输入合法的邮箱地址')]) ip = StringField('ip',validators=[IPAddress(message='输入合法的ipv4地址')]) url = StringField('url',validators=[URL(message='请输入正确的url')]) phone = StringField('手机号码',validators=[Regexp("[1][3-8][0-9]{9}$",flags=re.I|re.M|re.S,message='输入正确的手机号码')]) # re.I 不区分大小写 # re.S 使.匹配换行符 # re.M 改变 ^ 和 $ 作用 使之可以多行匹配 submit = SubmitField('submit') #自定义表单验证器 validate_ 死的 + 验证的属性名 当点击的时候 就会执行当前的自定义表单验证器 def validate_username(self,field): if self.username.data == 'zhangsan': raise ValidationError('当前用户已存在')
一、macro 宏的使用概述： 类似于python中的函数(1)主体结构{ % macro 宏名称([参数...]) % } ... { % endmacro % } {{ 宏名称([参数...]) }}实例{ % macro form(textname,text='text',name='',value='') % } {{ textname }} { % endmacro % } {{ form('用户名：',name='username') }} {{ form('密码：','password','userpass') }} {{ form('','submit',value='submit') }} (2)导入macrocommon/public_macro.htmlfrom ... import ... as ..{ % from 'common/public_macro.html' import form % } { % from 'common/public_macro.html' import form as f % } #起别名fimport ... as{ % import 'common/public_macro.html' as mymacro % }注意：宏的调用 必须在定义的下方来调用如果有形参且没有默认值则可以不传实参参数默认从左到右依次对应形参默认值 遵循默认值规则 （有默认值的放在右侧）关键字参数的使用 一样当导入的包名字过长，或者是当前文件中存在同名的变量或者函数,类名 为防止重名覆盖 使用 as 起别名二、模板继承1. extends格式：{ % extends '模板路径/模板名称.html' % }2. block对于父模板的代码的替换实例base.html(父模板) { % block head % } { % block meta % } { % endblock % } {# { % block title % }{{ title|default('base') }}{ % endblock % } #} { % block title % }base{ % endblock % } { % block link % } { % endblock % } { % block script % } { % endblock % } { % endblock % } 导航 { % block con % } { % endblock % } 底部栏 child.html(子模板){ % extends 'common/base.html' % } { % block title % } child { % endblock % } { % block style % } { { super() } } a{display:block;width:200px;height:200px;color:white;background-color:yellow;} a:hover{background-color:blue;} div{background-color:green;} { % endblock % } { % block con % } 我是子模板的内容部分 点击去百度 { % endblock % }注意： 如果子模板继承了父模板后代码缺失或者样式不起作用，查看是否调用了{ { super () } }，super调用父模板中被替换掉的代码和样式三、在flask中使用bootstrap(flask-bootstrap)(1)创建bootstrap模板对象from flask_bootstrap import Bootstrap app.config['BOOTSTRAP_SERVE_LOCAL'] = True #加载本地的bootstrap样式 否则从网络请求 bootstrap = Bootstrap(app)(2)使用flask-bootstrap的base模板{ % block doc -% } <html{ % block html_attribs % }{ % endblock html_attribs % }> { %- block html % } { %- block head % } { % block title % }{{title|default}}{ % endblock title % } { %- block metas % } { %- endblock metas % } { %- block styles % } { %- endblock styles % } { %- endblock head % } <body{ % block body_attribs % }{ % endblock body_attribs % }> { % block body -% } { % block navbar % } { %- endblock navbar % } { % block content -% } { %- endblock content % } { % block scripts % } { %- endblock scripts % } { %- endblock body % } { %- endblock html % } { % endblock doc -% }(3)自定义base 继承flask-bootstrap的base实例： 引用bootstrap的导航栏{ % extends 'bootstrap/base.html' % } { % block navbar % } Toggle navigation 首页(current) 发表博客 Submit 欢迎：... 登录 注册 个人中心 Action Another action Something else here 退出登录 { % endblock % } { % block content % } { % block pagecontent % } 就是主体内容 { % endblock % } { % endblock % }(4)子模板继承自定义的base模板{ % extends 'common/base.html' % } { % block title % } 首页 { % endblock % } { % block pagecontent % } 首页 { % endblock % }继承过程：子模板-----继承----->自定义的base -----继承---->flask-bootstrap的base模板四、自定义错误模板manage.py #捕获404 @app.errorhandler(404) def page_not_found(e): return render_template('error/error.html',title='404 NOT_FOUND',h1con='404页面找不到...',error=e) #捕获500 @app.errorhandler(500) def server_error(e): return render_template('error/error.html',title='500 SERVER_ERROR',h1con='当前服务繁忙 请稍后再次访问...')error/error.html{ % extends 'common/base.html' % } { % block title % } {{ title }} { % endblock % } { % block pagecontent % } {{ h1con }} { % if error % } {{ error }} { % endif % } { % endblock % }五、静态文件加载概述： 上传文件，比如网站用到的图片，视频，音频，js，css...目录结构project/ templates/ #模板目录 static/ #静态资源目录 img/ js/ css/ upload/ manage.py加载静态资源实例： 设置页面图标{ % extends 'common/base.html' % } { % block title % } 首页 { % endblock % } { % block head % } { { super () } } { % endblock % } { % block pagecontent % } 首页 { % endblock % }注意：static视图函数是flask为我们提供的，作用是构造出到static静态文件夹的路径，想加载哪个文件就使用参数filename='路径/文件名称'_external=True 显示绝对路径{{ url_for('static',filename='img/timg.jpeg',_external=True) }}六、视图函数传递参数到模板的方法(1) 直接在render_template()中传递@app.route('/') def index(): return render_template('index.html',title='title',....)(2) 通过字典的方式@app.route('/') def index(): return render_template('index.html',data={'title':'title',....})模板中调用{{ data.title }} (3) 使用 locals() 函数@app.route('/test/') def test(): name = '张三' age = 18 print(locals()) return render_template('index.html',**locals()) #将字典变成关键字参数模板中使用{{ name }} {{ age }}
一、模板简介概述： 模板就是按照一定的规则书写的负责展示的html页面，模板引擎提供特定规则、替换的工具。模板引擎：jinja2目录结构project/ templates/ #推荐使用该文件名，使用其他的名称需重新配置 manage.py模板渲染使用的方法(1)render_template(template_name,**context)响应一个html模板(2)render_template_string响应一段html代码 （适合响应简短的样式的响应）二、模板中语法的分类1. 变量 {{ 变量名称 }}视图传递给模板的数据遵循变量/标识符的命名规则关键字参数实例 可以给render_template 添加关键字参数,进行变量的传递@tem.route('/test/') def test(): h1con = '模板的变量' return render_template('test.html',h1=h1con) #渲染模板响应给用户 模板中使用 {{ h1 }} 注意： 如果给定的变量不存在则什么都不插入（什么都没有）2. 标签 {% 标签名称 %}就是python中的逻辑的判断循环/if...三、过滤器（相当于python中的函数）过滤器使用管道符 | 添加上函数和值进行使用abs 返回一个数值的绝对值default 默认值 注意： 默认只有当前变量不存在才执行默认值，将参数boolean改为True，则bool和undefined都执行默认值实例 boolean默认为Falsefirst 返回序列中的第一个元素last 返回序列中的最后一个值format 格式化输出{{ "我叫%s 我今年%d岁了 我的存款为%.2f元"|format('张三',18,123) }}length 值长度join 拼接{{ ['a','b','c']|join('') }} safe 对视图传递的html代码进行转义解析 （默认不解析 转义掉）int 转换整形float 浮点string 字符串list 转换成列表upper 转换成大写lower 转换成小写replace 替换 {{ 'abcd'|replace('a','x') }}striptags 取出html标签trim 去除两侧空格四、标签语法格式：{% %} 注意： 标签有开始，就要有结束> < >= <= == != in not in and or(1) if elif else{% if 90<=data.grade<=100 %} 优秀 {#{% elif 80<=data.grade<=89 %}#} {% elif 80<=data.grade %} 良 {#{% elif 70<=data.grade<=79 %}#} {% elif 70<=data.grade %} 中 {% else %} 还可以 {% endif %}(2) for 循环{% for ... %}​ ...{% endfor %}迭代字典{% for k,v in data.items() %} {{ k }}----{{ v }} {% endfor %} {% for i in range(10) %} {{ i }} {% endfor %}搭配else{ % for i in abc % } {{ i }} { % else % } 执行else了 { % endfor % }注意： 当迭代的变量不存在则执行else获取当前迭代的状态变量说明loop.index当前迭代的索引 从1开始loop.index0当前迭代的索引 从0开始loop.first是否为第一次迭代loop.last是否为最后一次迭代loop.length迭代的长度注意： 没有break 和 continue五、注释多行注释{# 注释的内容 #}注意： 多行注释不能嵌套多行注释六、文件的包含 include概述：include语句可以把一个模板导入到另外的一个模板的指定位置，就相当于把另外的模板中的代码粘贴到了你的指定位置结构：{% include 路径/模板名称.html %}实例文件目录 project/ templates/ commom/ header.html footer.html index.html manage.pymanage.py@app.route('/') def index(): return render_template('index.html') #跳到首页模板common/header.html 导航 common/footer.html 底部栏 index.html Title {% include 'common/header.html' %} {% include 'common/footer.html' %}
一、flask-script扩展库简介：是一个flask终端运行的解析器，因为在项目完成以后 所有的代码都不应该有任何的修改 因为一旦有修改 就有可能带来bug导入： from flask_script import Manager使用from flask_script import Manager manager = Manager(app) ... if __name__ == '__main__': manager.run()启动的参数参数参数说明-h帮助-h主机-p端口号-d调试 显示出现的错误信息-r代码加载--threaded开启多线程在控制台输入启动命令运行程序python3 manage.py runserver -hpython3 manage.py runserver -h0.0.0.0 -p5000 -d -r --threaded #全部开启python3 manage.py runserver -d -r #开启调试和代码加载二、蓝本/蓝图（blueprint）概述：当项目的代码越来越多的时候 把所有代码 都放到一个文件中 是不合理的 要根据不同功能的视图函数 进行划分到不同的模块文件中使用新建一个user.pyfrom flask import Blueprint #导入蓝本 user = Blueprint('user',__name__) #参数1 当前蓝本的名字 参数2 所在模块 @user.route('/loign/') def login(): return 'login'在manage.py中注册蓝本,使之与当前的flask框架关联起来from user import user #从user.py模块中 导入user蓝本对象 app.register_blueprint(user) #注册蓝本 #http://127.0.0.1:5000/login/ app.register_blueprint(user,url_prefix='/user') #添加一个路由前缀 http://127.0.0.1:5000/user/login/蓝本之间的重定向from flask import Blueprint,url_for,redirect user = Blueprint('user',__name__) #实例化蓝本对象 参数1 为蓝本的名字 参数2为所在的模块 @user.route('/login/') def login(): # return '蓝本登录' return redirect(url_for('posts.send_posts')) #指定跳转到对应的蓝本的视图函数中三、当前flask对象的代理对象(current_app)获取app对象上的所有的配置app = Flask(__name__) #添加配置俩种方式 app.config['SECRET_KEY'] = '123' #给flask 添加了一个配置 secret_key app.name = 'zhangsan' #给flask 添加了一个配置 secret_key manager = Manager(app) #实例化并加载app获取配置信息from flask import Blueprint,current_app posts = Blueprint('posts',__name__) #获取配置的两种方式 @posts.route('/send_posts/') def send_posts(): # return '发帖子{}'.format(current_app.config['SECRET_KEY']) #获取secret_key的值 return '发帖子{}'.format(current_app.name) #获取name的值四、cookie 和 session附：cookie和session的区别和用法1.cookie原因： http协议 无状态协议在网站中，http请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后，第二次请求服务器依然不能知道当前请求是哪个用户。cookie的出现就是为了解决这个问题，第一次登录后服务器返回一些数据（cookie）给浏览器，然后浏览器保存在本地，当该用户发送第二次请求的时候，就会自动的把上次请求存储的cookie数据自动的携带给服务器，服务器通过浏览器携带的数据就能判断当前用户是哪个了。cookie存储的数据量有限，不同的浏览器有不同的存储大小，但一般不超过4KB。因此使用cookie只能存储一些小量的数据。(1) 设置cookieResponse.set_cookie( key, 键 value,值 max_age=None, #存活的时间 单位是秒 expires=None,#过期时间 path = '/',# cookie的有效路径 )实例#cookie的操作 @user.route('/set_cookie/') def set_cookie(): response = make_response('设置cookie') #构造响应 response.set_cookie('name','张三') #设置cookie 没有设置过期时间 # return '设置了cookie' #错误的响应 没有将cookie响应给用户 return response注意： 如果没有设置cookie的存活时间 则cookie的过期时间为 当前浏览会话结束（关闭浏览器）(2) 设置cookie 并设置过期时间#设置cookie并设置过期时间 @user.route('/set_cookie_timeout/') def set_cookie_timeout(): response = make_response("设置cookie并设置过期时间") #max_age 设置过期时间 response.set_cookie('name','zhangsan',max_age=10) #设置活10秒 #expires 设置过期时间 lifttime = time.time()+20 response.set_cookie('name','lisi',expires=lifttime) return response(3) 获取cookie#获取cookie @user.route('/get_cookie/') def get_cookie(): print(request.cookies) return 'cookie的值为{}'.format(request.cookies.get('name'))(4) 删除cookie#删除cookie @user.route('/del_cookie/') def del_cookie(): response = make_response('删除cookie') # response.delete_cookie('name') #删除key为 name的cookie response.set_cookie('name','',max_age=0) return response2.session概述：服务器要区分不同的用户请求 要通过浏览器的cookie来实现 在浏览器访问服务器的时候 会给当前的用户设置一个唯一的id号（sessionid） 用户每次在请求的时候 只需要携带着sessionid来请求我 就可以 服务器就可以根据不同的唯一sessionid来区分不同用户的请求注意： 要生成sessionid 就需要给加密种子（密钥）secret_key导入: from flask import session(1) 设置session#设置session @mysession.route('/set_session/') def set_session(): session['id'] = 1 session['username'] = '张三' return '设置session'(2) 设置session并设置过期时间#设置session并设置过期时间 @mysession.route('/set_session_timeout/') def set_session_timeout(): session.permanent = True #设置session持久化存储 current_app.permanent_session_lifetime = timedelta(seconds=100) #设置过期时间为100秒 session['id'] = 1 session['username'] = '张三' return '设置session及过期时间'(3) 获取session#获取session @mysession.route('/get_session/') def get_session(): print(session) return 'id值为:{} username为:{}'.format(session.get('id'),session.get('username'))(4) 删除session#删除session @mysession.route('/del_session/') def del_session(): # session.pop('username') # session.pop('id') session.clear() return '删除了session值'五、钩子函数概述： 介于请求和响应之间，过滤掉不安全或者无意义的请求。作用： 使用钩子函数进行过滤。如果没有登录，但是访问了需要登录的路由地址的时候，这时就禁止访问或自动跳转到登录页，否则就可以正常访问。1.在启动文件（manage.py）中使用钩子函数钩子函数功能描述before_first_request第一次请求之前before_request每次请求之前after_request每次请求之后teardown_request每次请求之后 即使请求有异常出现实例#钩子函数 第一次请求之前 @app.before_first_request def before_first_request(): print('before_first_request') #每次请求之前 @app.before_request def before_request(): print(request.path) print(request.host) print(request.headers.get('User-Agent')) if request.host != '127.0.0.1:5000' or not request.headers.get('User-Agent'): abort(500) print('before_request') #请求之后 @app.after_request def after_request(r): print('after_request') return r #请求之后 @app.teardown_request def teardown_request(r): print('teardown_request') return r2.在蓝本中使用钩子函数钩子函数功能描述before_app_first_request第一次请求之前before_app_request每次请求之前after_app_request每次请求之后teardown_app_request每次请求之后 即使请求有异常出现实例#钩子函数 第一次请求之前 @middle.before_app_first_request def before_first_request(): print('before_first_request') #每次请求之前 @middle.before_app_request def before_request(): print(request.path) print(request.host) print(request.headers.get('User-Agent')) if request.host != '127.0.0.1:5000' or not request.headers.get('User-Agent'): abort(500) print('before_request') #请求之后 @middle.after_app_request def after_request(r): print('after_request') return r #请求之后 @middle.teardown_app_request def teardown_request(r): print('teardown_request') return r
Flask框架学习的整体内容1.Flask基础2.模板引擎3.flask表单4.文件上传5.邮件发送6.模型(model)7.拆分MVT8.项目的需求分析(博客)9.Flask项目10.flask部署11.flask-resfulFlask web框架分为MVCM: 模型 （数据的操作）V : 视图C : 控制器python中 MVTM: 模型 （数据的操作）V : 控制器T: 视图web 架构B/S浏览器/服务器C/S客户端/服务器Flask模块概述： flask是一个非常小的python web框架，被称为微型框架，只提供了一个强健的核心 ，其它功能都是通过第三方扩展库来实现的。组成： 路由、模板、模型、控制器、wsgi系统、jinja2模板引擎一、完整的运行(1) 实例from flask import Flask #导入Flask app = Flask(__name__) #实例化flask类 __name__ 必传参数 当前执行的模块 #路由地址 请求当前的路由 127.0.0.1：5000 @app.route('/') def index(): #执行请求的函数 控制器 return 'hello flask' #响应 响应一段字符串 if __name__ == '__main__': #执行的模块 只在主程序执行 app.run() #运行flask(2) 路由地址@app.route('/地址/')def 函数名称([参数...]):​ pass请求地址为：http://ip地址:端口号/地址/(3) app.run参数参数参数说明debug是否开启调试模式 默认False 开启以后自动重新加载代码threaded开启多线程 默认是不开启的 Truehost主机名 默认是127.0.0.1 在其它反问的时候 127.0.0.1-127.255.255.254 本地ip 0.0.0.0本地，网络都可以请求port端口号 默认5000实例：if name == 'main': #执行的模块 只在主程序执行 app.run( host='0.0.0.0',port=5001,debug=True,threaded=True) #运行flaskhttp://10.0.121.138:5001/http://127.0.0.1:5001/二、视图函数(1) 无参路由@app.route('/') def index(): pass(2) 带一个参数的路由#传递一个参数的路由 @app.route('/args/<page>/') def args(page): print(type(page)) return 'page的值为{}'.format(page)注意： 路由值都为字符串类型(3) 传递多个参数#传递多个参数 类型1： #http://127.0.0.1:5000/manyarg/1/2/ @app.route('/manyarg/<arg1>/<arg2>/') def manyArg(arg1,arg2): print(type(arg1),type(arg2)) return 'arg1的值为{} arg2的值为{}'.format(arg1,arg2) #传递多个参数 类型2： #http://127.0.0.1:5000/manyarg/1_2/ @app.route('/manyarg/<arg1>_<arg2>/') def manyArg(arg1,arg2): print(type(arg1),type(arg2)) return 'arg1的值为{} arg2的值为{}'.format(arg1,arg2) #运行结果：arg1的值为1 arg2的值为2(4) 限制参数的类型intstringpathfloat@app.route('/argtype/<int:arg>/') #只能传递整形 @app.route('/argtype/<string:arg>/') #字符串 默认就是字符串 @app.route('/argtype/<path:arg>/') #路径 将/不再认为是分隔符 而是值的一部分 其实也是字符串类型 @app.route('/argtype/<float:arg>/') #浮点形 def argType(arg): print(type(arg)) print(arg) return '参数类型'注意：路由地址末尾的‘/’ 建议都加上，如果在访问需要‘/’的路由的时候没有添加‘/’的话，浏览器会自动添加上‘/’参数写在<>内，<>内的参数和前面的路由地址构造出一个全新的路由地址参数类型默认为 string三、响应 response(1) 指定响应的状态码实例@app.route('/') def index(): return '值',404 #code状态码 必须是标准库中的(2) 通过make_response 构造响应(make_response)#make_response 构造响应 @app.route('/response/') def res(): # response = make_response('make_response构造响应') response = make_response('make_response构造响应',500) #将响应的状态码 设置为 500 return response四、重定向( url_for,redirect)(1) redirect 的使用redirect不带参数的跳转@app.route('/') def index(): return 'index' @app.route('/login/') def login(): return redirect('/') #参数为跳转的路由地址redirect带参数的跳转@app.route('/test/<arg1>/<arg2>/') def test(arg1,arg2): return 'test' @app.route('/login/') def login(): return redirect('/test/1/2/') #参数为跳转的路由地址(2) url_for 根据 视图函数名称 反向构造出路由地址@app.route('/'): #不带参数 return url_for('index') # / return url_for('indexx') #当前indexx视图函数不存在 #Could not build url for endpoint 'indexx'. Did you mean 'index' instead? #带参数 return url_for('test',arg1=1,arg2=2) # /test/1/2/ return url_for('test',arg1=1,arg2=2) # 如果 arg1和arg2是路由地址的参数 则拼凑的地址为 /test/1/2/ 如果不是则 /test/?arg1=1&arg2=2(3) redirect和url_for的组合重定向@app.route('/test/') def test(): return redirect(url_for('index')) # 跳转到 首页视图 /注意： 如果视图函数不存在则会报错HTTP状态码所代表的含义消息成功重定向请求错误服务器错误五、abort 抛出http标准状态码中的错误(abort)@app.route('/test/') def test2(): print('404') abort(404)注意： 类似raise 上面的代码正常执行 下面代码不再执行捕获http状态码的错误#捕获404状态码的错误 @app.errorhandler(404) def page_not_found(e): #e为错误的信息 return '404-------{}'.format(e)六、请求(request)作用：获取用户请求所带的数据概述：浏览器发送到服务器的所有请求被flask接收以后,创建出request对象,request对象用在视图函数中,用于获取请求的数据request对象由flask创建，想要获取请求数据，直接导入模块即可request属性：url 获取完整请求的url地址 print('获取完整请求的url地址',request.url)base_url 去掉get传参的urlprint('去掉get传参的url',request.base_url)host_url 只有主机地址和端口号 print(' 只有主机地址和端口号',request.host_url)path 请求的路由地址print('请求的路由地址',request.path)method 请求的方法 print('请求的方法',request.method)args 请求的get传参print(' 请求的get传参',request.args) print(' 请求的get传参',request.args['name'])#当返回的args中不存在name时报错 print(' 请求的get传参',request.args.get('name')) #当返回的args中不存在name时不报错 print(' 请求的get传参',request.args.getlist('name'))#args存在多个name时，该方法返回所有的name对应的值--列表form 获取表单post传递过来的数据 print('获取完整请求的url地址',request.form)files 文件上传 print('获取完整请求的url地址',request.files)headers 获取请求的头信息 想要获取哪个 使用request.headers.get(key)进行获取print('请求头信息',request.headers.get('User-Agent')) print('请求头信息',request.headers.get('Host'))cookies 获取cookieprint('获取请求过来的cookie',request.cookies) print('获取请求过来的cookie',request.cookies.get('sessionid'))json 获取请求过来的json数据print('获取请求过来的json数据',request.json)实例@app.route('/') def index(): print('获取完整请求的url地址',request.url) print('去掉get传参的url',request.base_url) print(' 只有主机地址和端口号',request.host_url) print('请求的路由地址',request.path) print('请求的方法',request.method) print('请求的get传参',request.args) print('请求的get传参',request.args['name']) print('请求的get传参',request.args.get('name')) print('请求的get传参',request.args.getlist('name')) print('请求的get传参',request.args['age']) print('请求的get传参',request.args.get('age')) print(' 获取表单post传递过来的数据',request.form) print('获取文件上传',request.files) print('请求头信息',request.headers.get('User-Agent')) print('请求头信息',request.headers.get('Host')) print('获取请求过来的cookie',request.cookies) print('获取请求过来的cookie',request.cookies.get('sessionid')) print('获取请求过来的json数据',request.json) return 'index'