Flask剖析
web框架与WSGI协议
Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)
为python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。
jinja2 & Werkzeug
-
jinja2一个功能齐全的模板引擎
-
Werkzeug是一个WSGI工具包,它可以作为web框架的底层库
三者关系
Werkzeug用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理。将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。
flask项目
flask项目框架
/项目文件夹 /models /static /templates app.py
数据库的连接
-
配置数据库
# 数据库配置信息 DB_USERNAME = 'root' #数据库系统账号 DB_PASSWORD = 'root' #数据库系统密码 DB_HOST = 'localhost' DB_PORT = 3306 #数据库系统端口号 DB_NAME = 'forum' #数据库名称 # 数据库连接URI DB_URI = f'mysql+pymysql://{DB_USERNAME}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8' # SQLAlchemy配置 SQLALCHEMY_DATABASE_URI = DB_URI SQLALCHEMY_TRACK_MODIFICATIONS = False # 关闭对模型修改的监控
2.数据库初始化
from flask_sqlalchemy import SQLAlchemy db = SQLALchemy() db.init_app(app)
创建两个py文件(config.py ,exts.py)分别存放配置信息和组件初始化
在app.py中引入配置文件和组件初始化,将两者绑定
#导入文件 import config from exts import db app.config.from_object(config) # 加载配置 #绑定信息 db.init_app(app) # 初始化数据库
3.数据库模型定义
class Model(db.Model):#Model是自己定义的表单模型 __table__ = 'demo' #给表单指定名称 #属性信息 id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(50), nullable=False)
在models文件夹中创建单独的py文件,存放一类的数据库模板
from exts import db class Model(db.Model):#Model是自己定义的表单模型 __table__ = 'demo' #给表单指定名称 #属性信息 id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(50), nullable=False)
前后端连接
1.通过render_template
返回 html 模板
2.通过返回API接口
@bp.route('/login', methods=['GET', 'POST']) #定义路由地址 def login(): if request.method == 'GET': #返回html模板 --前后端不分离 return render_template("front/login.html") else: #通过表单验证数据 form = LoginForm(request.form) if form.validate(): email = form.email.data password = form.password.data remember = form.remember.data user = UserModel.query.filter_by(email=email).first() if not user: return restful.params_error("邮箱或密码错误!") if not user.check_password(password): return restful.params_error("邮箱或密码错误!") session['user_id'] = user.id permissions = [] if user.is_staff: token =create_access_token(identity=user.id) for attr in dir(Permission): if not attr.startswith("_"): permission = getattr(Permission, attr) if user.has_permission(permission): permissions.append(attr.lower()) if remember == 1: session.permanent = True user = user.to_dict() user['permissions'] = permissions #API接口 return restful.ok(data={"token": token, "user": user}) else: return restful.params_error(message=form.messages[0])
获取HTTP数据
-
通过
request.args.get
获取查询参数:board_id = request.args.get("bd", type=int, default=None)
-
来源: 查询参数(Query Parameters),即 URL 中
?
后面的部分,例如http://example.com/path?bd=123
。 -
用途: 通常用于获取 URL 中的参数,这些参数是可选的,并且可以用于过滤、分页等操作。
-
参数:
-
"bd"
:查询参数的键名。 -
type=int
:将获取到的值转换为整数类型。 -
default=None
:如果查询参数不存在,则返回默认值None
。
-
-
-
通过
request.form
获取表单数据:form = RegisterForm(request.form)
-
来源: 表单数据(Form Data),即通过 POST 请求提交的表单数据。
-
用途: 通常用于处理用户提交的表单数据,例如注册表单、登录表单等。
-
参数:
-
request.form
:包含表单数据的字典对象。 -
RegisterForm
:一个表单类,用于验证和处理表单数据。
-
-
总结:
-
request.args.get
用于从 URL 查询参数中获取数据,适用于 GET 请求。 -
request.form
用于从 POST 请求的表单数据中获取数据,适用于 POST 请求。
获取数据方法分类
-
获取查询参数(Query Parameters):
from flask import request @app.route('/example') def example(): param = request.args.get('key', default=None, type=str) return f'Query parameter: {param}'
-
获取表单数据(Form Data):
from flask import request @app.route('/submit', methods=['POST']) def submit(): form_data = request.form['key'] return f'Form data: {form_data}'
-
获取 JSON 数据(JSON Data):
from flask import request @app.route('/json', methods=['POST']) def json_endpoint(): json_data = request.get_json() return f'JSON data: {json_data}'
-
获取文件上传(File Uploads):
from flask import request @app.route('/upload', methods=['POST']) def upload_file(): file = request.files['file'] file.save('/path/to/save/file.ext') return 'File uploaded successfully'
-
获取请求头(Request Headers):
from flask import request @app.route('/headers') def headers(): user_agent = request.headers.get('User-Agent') return f'User-Agent: {user_agent}'
-
获取路径参数(Path Parameters):
from flask import request @app.route('/user/<int:user_id>') def user_detail(user_id): return f'User ID: {user_id}'
-
获取 Cookie 数据:
from flask import request @app.route('/cookie') def cookie(): cookie_value = request.cookies.get('cookie_name') return f'Cookie value: {cookie_value}'
-
获取请求方法(Request Method):
from flask import request @app.route('/method', methods=['GET', 'POST']) def method(): method = request.method return f'Request method: {method}'
保存图片封装函数模板
def update_media(_path): form = UploadForm(request.files) if form.validate(): image = form.image.data filename = image.filename # xxx.png,xx.jpeg _, ext = os.path.splitext(filename) filename = md5((g.user.email + str(time.time())).encode("utf-8")).hexdigest() + ext image_path = os.path.join(current_app.config[_path], filename) image.save(image_path) else: message = form.messages[0] return restful.params_error(message=message)
组件
flask_wtf
from werkzeug import secure_filename #secure_filename 是一个用于安全处理文件名的函数 ''' secure_filename 函数会做以下几件事: 1.去除路径分隔符:如 / 和 \。 2.去除文件名中的控制字符:这些字符可能会被操作系统解释为特殊命令。 3.确保文件名是有效的:比如在 Windows 上,某些特定的文件名是不允许的(如 CON, PRN, AUX 等)。 ''' filename = secure_filename("/path/to/file.txt") print(filename) # 输出 "file.txt"
FileAllowed 可与 Flask-Uploads 协同工作,例如:
from flask.ext.uploads import UploadSet, IMAGES from flask_wtf import Form from flask_wtf.file import FileField, FileAllowed, FileRequired images = UploadSet('images', IMAGES) class UploadForm(Form): upload = FileField('image', validators=[ FileRequired(), FileAllowed(images, 'Images only!') ])
它也可以在没有 Flask-Uploads 的情况下工作。你需要向 FileAllowed 传递扩展名:
class UploadForm(Form): upload = FileField('image', validators=[ FileRequired(), FileAllowed(['jpg', 'png'], 'Images only!') ])
要对所有视图函数启用 CSRF 保护,你需要启用 CsrfProtect 模块:
from flask_wtf.csrf import CsrfProtect CsrfProtect(app)
需要为 CSRF 指定一个密钥。通常,这与你的 Flask 应用 SECRET_KEY
一致。
flask_mail
MAIL_SERVER = 'smtp.qq.com' MAIL_PORT = 587 MAIL_USE_TLS = True ''' MAIL_PORT = 465 MAIL_USE_SSL = True ''' MAIL_USERNAME = '2407992583@qq.com' MAIL_PASSWORD = # MAIL_DEFAULT_SENDER = #
TLS 163邮箱验证失败;qq邮箱验证成功
def email_captcha(): # /email/captcha?email=xx@xx.com email = request.args.get("email") if not email: return restful.params_error(message="请先传入邮箱!") source = list(string.digits) captcha = "".join(random.sample(source, 6)) subject = "【知识论坛】注册验证码" body = f"【知识论坛】您的注册验证码为{captcha}" # Celery 将 send_mail 任务添加到任务队列中,并传递相应的参数。 current_app.celery.send_task("send_mail", (email, subject, body)) #将数据存储到缓存中 cache.set(email, captcha) return restful.ok(message="邮件发送成功!")
通过redis 缓存提高邮件发送效率
''' Celery的redis配置 ''' CELERY_BROKER_URL = "redis://localhost:6379/0" CELERY_RESULT_BACKEND = "redis://localhost:6379/0" ''' Flask-Caching 的配置 ''' CACHE_TYPE = 'RedisCache' CACHE_DEFAULT_TIMEOUT = 300 CACHE_REDIS_HOST = '127.0.0.1' CACHE_REDIS_PORT = 6379