Flask相比于django更加的轻量级,最核心的两个模块: Werkzeug(路由模块),模板引擎则使用 Jinja2。Flask不像django自带各种模块,用于一些小中型的项目开发。
Flask诞生于2010年,是Armin ronacher(人名)用 Python 语言基于 Werkzeug 工具箱编写的轻量级Web开发框架。
Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login,数据库Flask-SQLAlchemy),都需要用第三方的扩展来实现。比如可以用 Flask 扩展加入ORM、窗体验证工具,文件上传、身份验证等。Flask 没有默认使用的数据库,你可以选择 MySQL,也可以用 NoSQL。
其 WSGI 工具箱采用 Werkzeug(路由模块),模板引擎则使用 Jinja2。这两个也是 Flask 框架的核心。
官网: https://flask.palletsprojects.com/en/1.1.x/
官方文档: http://docs.jinkan.org/docs/flask/
Flask常用第三方扩展包:
可以通过 https://pypi.org/search/?c=Framework+%3A%3A+Flask 查看更多flask官方推荐的扩展
创建一个虚拟环境
mkvirtualenv flask -p python3 # -p:指定python3
安装
pip install flask==0.12.5
创建一个目录,并在pycharm中打开
mkdir -r /home/wangfan/flask/flask_demo01
创建一个main.py,并设置依赖环境(pycharm:file->settings)
写flask框架的主程序
#导入Flask类
from flask import Flask
# 1.创建应用对象
app = Flask(__name__)
# 2.指定路由
@app.route('/')
def index(): # 3.视图函数
return "hello,world"
# 4. 返回字符串
if __name__ == '__main__':
# 5. 启动项目
app.run(debug=True, port=8001, host='0.0.0.0') # 可以指定参数,由wsgiref模块提供
代码分析:
创建应用对象时,传入参数的说明
额外加载配置的说明(1.自定义类2.通过app.config.from_object(类名)
注册),也有其他方式如下:
# 导入Flask类
from flask import Flask
"""
import_name Flask程序所在的包(模块),传 __name__ 就可以
其可以决定 Flask 在访问静态文件时查找的路径
static_path 静态文件访问路径(不推荐使用,使用 static_url_path 代替)
static_url_path 静态文件访问路径,可以不传,默认为:/ + static_folder
static_folder 静态文件存储的文件夹,可以不传,默认为 static
template_folder 模板文件存储的文件夹,可以不传,默认为 templates
"""
app = Flask(import_name=__name__)
# 编写路由视图
# flask的路由是通过给视图添加装饰器的方式进行编写的。当然也可以分离到另一个文件中。
# flask的视图函数,flask中默认允许通过return返回html格式数据给客户端。
@app.route('/')
def index():
return "hello world
"
# 加载项目配置
class Config(object):
# 开启调试模式
DEBUG = True
# flask中支持多种配置方式,通过app.config来进行加载,我们会这里常用的是配置类
app.config.from_object( Config )
# 指定服务器IP和端口
if __name__ == '__main__':
# 运行flask
app.run(host="0.0.0.0", port=5000)
1.路由: 一种访问地址[url]和应用程序[视图]进行一对一绑定的映射关系
2.往往在开发中,我们所说的路由,其实通常指代完成路由绑定关系的路由类
3.路由和视图的名称必须全局唯一,不能出现重复,否则报错。
4.可以通过参数method指定请求方法
指定参数进行传递
from flask import Flask
app = Flask(__name__)
# 指定参数进行传递-->django路由:有名分组
@app.route('/user/' , methods=['get']) # 只能发送get请求
def index(user_name):
return "hello,%s" % (user_name)
if __name__ == '__main__':
app.run()
1.int:接受正整数
2.float:接受正浮点值
3.uuid:接受UUID(通用唯一识别码)字符串 xxxx-xxxx-xxxxx-xxxxx
4.string:默认类型,接受不带斜杠的任何文本
5.path:接收string
但也接受斜线
from flask import Flask
app = Flask(__name__)
# 内置路由参数转换器
@app.route('/user2/' )
def user(user_id):
return 'user_id:%s' % user_id
if __name__ == '__main__':
app.run()
原码:(werkzeug.routing.py)
DEFAULT_CONVERTERS = {
"default": UnicodeConverter,
"string": UnicodeConverter,
"any": AnyConverter,
"path": PathConverter,
"int": IntegerConverter,
"float": FloatConverter,
"uuid": UUIDConverter,
}
也叫正则匹配路由参数.
app.url_map.converters['别名']=自定义类名
)from flask import Flask
app = Flask(__name__)
# 1.引入BaseConverter路由参数转换器基类
from werkzeug.routing import BaseConverter
# 2.自定义路由参数转换器
class MobileConverter(BaseConverter):
regex = r'1[3-9]\d{9}'
def __init__(self, map, *args, **kwargs):
super().__init__(map) # map相当于django中的urlpatterns(所有路由的列表)
# 3.注册路由参数转换器到app对象中
app.url_map.converters['mob'] = MobileConverter # 起别名为mob
@app.route(rule='/user3/' )
def user3(user_mobile):
return '%s' % user_mobile
if __name__ == '__main__':
app.run()
相当与django中的re_path()
与2.4中不同的是:正则规则,在路径上写直接当参数传至自定义类中
两种方法的本质为:改父类的regex属性
其中,注意传参时加上引号
from flask import Flask
app = Flask(__name__)
# 1.引入BaseConverter路由参数转换器基类
from werkzeug.routing import BaseConverter
# 自定义类
class MobileConverter2(BaseConverter):
def __init__(self, map, *args, **kwargs):
self.regex = args[0]
super().__init__(map)
# 注册
app.url_map.converters['mob2'] = MobileConverter2
# 传参 (注意传参时加上引号)
@app.route(rule=r'/user4/' )
def user4(user_id):
return '%s' % user_id
if __name__ == '__main__':
app.run()
通过app.add_url_rule(rule='路径',view_func=视图函数)
实现
from flask import Flask
app = Flask(__name__)
def index():
return "ok"
# 也可以让路由注册和视图进行分离
app.add_url_rule(rule="/",view_func=index)
if __name__ == '__main__':
app.run(debug=True)
属性 | 说明 | 类型 |
---|---|---|
data | 记录请求体的数据,并转换为字符串 只要是通过其他属性无法识别转换的请求体数据 最终都是保留到data属性中 |
bytes类型 |
form | 记录请求中的html表单数据 | MultiDict |
args | 记录请求中的查询字符串,也可以是query_string | MultiDict |
cookies | 记录请求中的cookie信息 | Dict |
headers | 记录请求中的请求头 | EnvironHeaders |
method | 记录请求使用的HTTP方法 | GET/POST |
url | 记录请求的URL地址 | string |
files | 记录请求上传的文件列表 | * |
json | 记录ajax请求的json数据 | json |
ImmutableMultiDict:这个类就是一个字典的子类,我们可以称之为类字典对象,所以可以通过字典的操作来使用(思路来源:from collection import OrderedDict)
文档: http://docs.jinkan.org/docs/flask/api.html#flask.request
原码位置:from flask.app import Request
引入:from flask import Flask,request
from flask import Flask,request
app = Flask(__name__)
@app.route(rule='/', methods=['get', 'post'])
def index():
print('查询字符串', request.args)
'''
ImmutableMultiDict([
('name', 'xyw'),
('hobby', '1'),
('hobby', '2'),
('hobby', '3')
])
'''
print(request.args.get('name')) # xyw
print(request.args.getlist('hobby')) # ['1', '2', '3']
print(request.args.to_dict(flat=False)) # 转成普通字典,列表 {'name': ['xyw'], 'hobby': ['1', '2', '3']}
print(request.args.to_dict(flat=True)) # 转成普通字典,保留第一个 {'name': 'xyw', 'hobby': '1'}
print('请求体数据(bytes类型)', request.data)
print('请求中表单数据', request.form) # ImmutableMultiDict([('name', 'xyw'), ('age', '48')])
print('请求头', request.headers)
'''
请求头 Authorization: jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo2LCJ1c2VybmFtZSI6Inh5dyIsImV4cCI6MTYwNTI3NDU4MiwiZW1haWwiOiIifQ.rc0Sjezl_uAa836wUzExyNru1VEE3qtes6-sJPWCv-0
User-Agent: PostmanRuntime/7.26.5
Accept: */*
Postman-Token: b636c222-ba16-4bae-95e3-1995b7344e5f
Host: 127.0.0.1:5000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------------------------145884211271048853446909
Content-Length: 266
'''
print('请求方法', request.method)
print('请求URL', request.url)
print('上传的文件', request.files)
print('请求的json数据', request.json)
print('是否是ajax请求', request.is_json) # True
return "hello,world"
if __name__ == '__main__':
app.run()
flask默认支持2种响应方式:
1.数据响应: 默认响应html文本,也可以返回 JSON格式,或其他格式
2.页面响应: 重定向
url_for 视图之间的跳转
响应的时候,flask也支持自定义http响应状态码
make_response
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/')
def index():
# return "hello,world"
return make_response("hello,world"
) # 与直接返回相同
if __name__ == '__main__':
app.run()
可以直接使用 jsonify 生成一个 JSON 的响应
from flask import Flask, make_response, jsonify
app = Flask(__name__)
@app.route('/')
def index():
data = {
'name': 'xyw',
'age': 18
}
return jsonify(data)
if __name__ == '__main__':
app.run()
redirect
from flask import Flask, make_response, jsonify, redirect
app = Flask(__name__)
@app.route('/user')
def user():
return redirect('http://www.baidu.com')
if __name__ == '__main__':
app.run()
1.可以直接填写自己 url 路径
2.也可以使用 url_for 生成指定视图函数所对应的 url
from flask import url_for
from flask import Flask, make_response, jsonify, redirect, url_for
app = Flask(__name__)
@app.route('/')
def index():
data = {
'name': 'xyw',
'age': 18
}
return jsonify(data)
@app.route('/user')
def user():
return redirect(url_for("index"))
if __name__ == '__main__':
app.run()
url_for()中加上endpoint='视图名', 参数=xx
from flask import Flask, make_response, jsonify, redirect, url_for
app = Flask(__name__)
@app.route('/' )
def index(userid):
return 'userid:%s' % userid
@app.route('/user')
def user():
return redirect(url_for(endpoint="index", userid=1))
if __name__ == '__main__':
app.run()
添加响应头,支持文件上传
from flask import Flask
app = Flask(__name__)
from flask import make_response,jsonify,request
@app.route(rule="/user", methods=["get","post"])
def user():
# 识别身份
if request.args.get("user") == "abc":
# 也可以返回图片,压缩包 等其他在浏览器能支持的数据,既可以支持显示图片,也可以支持显示
with open("2.jpg","rb") as f:
content = f.read()
response = make_response(content)
response.headers["Content-Type"] = "image/jpeg"
return response
else:
return "没有权限"
# 支持下载
# with open("123.zip", "rb") as f:
# response.headers["Content-Type"] = "application/zip"
# return response
@app.route("/")
def index():
return ""
if __name__ == '__main__':
app.run(debug=True)
添加自定义响应头,实现页面转跳
from flask import Flask,make_response
app = Flask(__name__)
@app.route("/user1")
def index1():
# 页面跳转
# return redirect("http://www.baidu.com") # 跳转到站外
# return redirect("/user") # 跳转到站内
# return redirect(url_for("user",user_id=100)) # 通过url_for指定视图名称,直接找到对应路由进行跳转
# 跳转的原理,实际就是利用HTML文档中的元信息
response = make_response()
response.headers["Location"] = "http://www.baidu.com" # 实现页面转跳
response.headers["Company"] = "oldboy" # 自定义响应头
response.status_code = 302 # 自定义响应状态吗
return response
if __name__ == '__main__':
app.run(port=8000)
from flask import Flask,request,make_response,Response,jsonify
app = Flask(__name__)
@app.route("/")
def index():
"""返回html数据"""
# return "ok"
# return make_response("ok") # 上面的代码是这段代码的简写
# return Response("ok") # 上面make_response本质上就是Response
"""返回json格式数据"""
# data = {"name":"xiaoming","age":13}
# return jsonify(data)
"""返回其他类型数据"""
# 关于Response常用的参数
# Response(response="内容", status="http响应状态码",headers=自定义响应头,mimetype="数据格式")
# return Response(response="ok",status=201,headers={"company":"hello"})
# 返回图片信息
with open('./1.zip',"rb") as f:
content=f.read()
# 判断权限,身份...
return Response(response=content,mimetype="application/zip")
if __name__ == '__main__':
app.run(debug=True)
产生原因:
http协议(基于tcp)的特点:1 无连接(短连接);2 无状态.
http协议不会记录客户端和服务端的任何信息,导致服务端和客户端不能维持会话
正是由于http无状态的特性,所以出现了cookie(一种浏览器技术).请求头健值对.
工作原理:
浏览器访问服务端,带着一个空的cookie,然后由服务器产生内容,浏览器收到相应后保存在本地;
当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。
from flask import Flask, request, make_response, session
app = Flask(__name__)
@app.route('/set_cookie')
def set_cookie():
# 设置cookie
response = make_response('ok')
response.set_cookie('username', 'xyw', 30) # key=键,value=值,max_age=有效时间(秒)
response.set_cookie('age', '108') # 如果cookie没有设置过期时间,则默认过期为会话结束过期
# 会话结束:浏览器关闭;意味着下一个站点下同变量名的cookie会被覆盖
return response
@app.route('/get_cookie')
def get_cookie():
# 获取cookie
print(request.cookies)
print(request.cookies.get('username'))
return 'ok'
@app.route('/del_cookie')
def del_cookie():
# 删除cookie
response = make_response('ok')
response.set_cookie('age', '', 0) # 设置有效时间覆盖原有的cookie达到删除的目的
return response
if __name__ == '__main__':
app.run(port=8002)
产生原因:
1.cookie是明文存储的
2.大小限制:
Cookie大小上限为4KB;
一个服务器最多在客户端浏览器上保存20个Cookie;
一个浏览器最多保存300个Cookie,因为一个浏览器可以访问多个服务器。
特点:
cookie中放的数据是密文的
数据存在服务端,没有大小上限
一个浏览器对应一个服务端,就是一个session
from flask import Flask, request, make_response, session
app = Flask(__name__)
# 需要配置一个secret_key用于session加密
class Config():
SECRET_KEY = '123456'
app.config.from_object(Config)
@app.route('/set_session')
def set_session():
# 设置session
# session 存在服务器的缓存中,支持python基本数据类型作为值
session['username'] = 'xyw'
session['info'] = {
'age': 11,
'sex': False
}
'''
浏览器中cookies中:
session:eyJpbmZvIjp7ImFnZSI6MTEsInNleCI6ZmFsc2V9LCJ1c2VybmFtZSI6Inh5dyJ9.X7YptA.J4GTqRRgsnt2ijZJLIhV5X-3pq8
'''
return 'ok'
@app.route('/get_session')
def get_session():
# 获取session
ret1 = session.get('username')
ret2 = session.get('info')
return '%s+%s' % (ret1, ret2)
@app.route('/del_session')
def del_session():
# 删除session
try:
del session['username']
# session.clear() # 删除所有
except:
pass
'''
删除了之后在获取得到:
None+{'age': 11, 'sex': False}
'''
return 'ok'
if __name__ == '__main__':
app.run(port=8002)
在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:
为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设置的功能,即请求钩子。
请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子:
from flask import Flask
app = Flask(__name__)
@app.before_first_request
def first_request():
print('1.项目启动以后,首次请求后执行')
@app.before_request
def before_request():
print('2.每次请求时,执行')
@app.after_request
def after_request(response):
# 会将响应结果传递进来
print('4.视图执行之后,执行')
return response
@app.teardown_request
def have_error(exc):
# 用来接收异常
print('5.在after_request执行后,执行')
'''
1. 在debug=False时,才能接收错误被执行,在debug=True时,只执行到有错误的那一行
2. 没有异常时,在after_request执行后,执行
3. 在有异常时,在有异常的那一行执行后,立即执行,(视图函数,after_request不在执行)
'''
print(exc)
@app.route('/')
def index():
1 / 0
print('3.视图函数执行了')
return 'ok'
if __name__=='__main__':
app.run(debug=False, port=8000)
代码执行效果:
有异常时:
执行上下文:
1.请求上下文 : request;session(来自于客户端)
2.应用上下文 : current_app;g(flask 应用程序运行过程中,保存的一些配置信息)
两者区别:
请求上下文:保存了客户端和服务器交互的数据,一般来自于客户端。
应用上下文:flask 应用程序运行过程中,保存的一些配置信息,比如路由列表,程序名、数据库连接、应用信息等
request:封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get(‘user’),获取的是get请求的参数。
session:用来记录请求会话中的信息,针对的是用户信息。举例:session[‘name’] = user.id,可以记录用户信息。还可以通过session.get(‘name’)获取用户信息。
from flask import Flask, current_app, g
'''
执行上下文:
1.请求上下文 : request;session(来自于客户端)
2.应用上下文 : current_app;g(flask 应用程序运行过程中,保存的一些配置信息)
'''
app = Flask(__name__)
@app.route('/')
def index():
print(current_app.config) # 获取当前项目的所有配置信息
print(current_app.url_map) # 获取当前项目的所有路由信息
func()
print(g.name)
return 'ok'
@app.before_request
def before_request():
g.name = 'xyw'
文档: https://flask-script.readthedocs.io/en/latest/
这个模块的作用可以让我们通过终端来控制flask项目的运行,类似于django的manage.py
安装:
pip install flask-script
from flask import Flask
from flask_script import Manager, Command, Option
app = Flask(__name__)
# 使用flask_script启动项目
manage = Manager(app)
if __name__ == '__main__':
'''
manager 自带两个命令:runserver/shell
端口和域名不写,默认为127.0.0.1:5000
python 04Flask-Script.py runserver
通过-h设置启动域名,-p设置启动端口
python 04Flask-Script.py runserver -h127.0.0.1 -p8888
'''
manage.run()
1.定义一个类 继承Command
2.添加参数option_list
3.添加run方法
4.将自定义的类加到终端脚本工具中(add_command)
from flask import Flask
from flask_script import Manager, Command, Option
app = Flask(__name__)
@app.route('/')
def index():
return 'ok'
# 1.定义一个类 继承Command
class HelloCommand(Command):
"""a simple example"""
option_list = [ # 2.添加参数
Option('--name', '-n', help='名称'),
Option('--num', '-m', help='数量')
]
# 3.添加run方法
def run(self, name, num): # 输入命令时,执行
print('name=%s' % name)
print(num)
print('hello,world')
# 使用flask_script启动项目
manage = Manager(app)
# 4.将自定义的类加到终端脚本工具中(add_command)
manage.add_command('hello', HelloCommand)
if __name__ == '__main__':
manage.run()
from flask import Flask
from flask_script import Manager, Command, Option
app = Flask(__name__)
@app.route('/')
def index():
return 'ok'
# 使用命令生成目录
import os
class BluePrintCommand(Command):
option_list = [
Option('--name', '-n', help='蓝图名称')
]
def run(self, name=None):
if name is None:
print('蓝图名称不能为空')
return
if not os.path.isdir(name):
os.mkdir(name)
open('%s/views.py' % name, 'w')
open('%s/models.py' % name, 'w')
with open('%s/urls.py' % name, 'w') as f :
f.write('''from . import views
urlpatterns = [
]
''')
manage.add_command('bule', BluePrintCommand)
if __name__ == '__main__':
manage.run()
使用Flask提供的render_template
函数,该函数封装了该模板引擎
它的第一个参数为页面的文件名,后面的参数为模板中变量对应的真实值(键值对)
1.创建应用对象的时候,添加template_folder参数(存放页面的目录位置)
2.使用Flask提供的render_template函数
main.py:
from flask import Flask, render_template
# 1.创建应用对象的时候,添加template_folder参数(存放页面的目录位置)
app = Flask(__name__,
template_folder='templates', # html页面
static_folder='static', # 静态资源
static_url_path='/static' # 提供给外界的访问路径
)
# 基本使用
@app.route('/')
def index():
data = {}
data['title'] = 'hello,world'
data['num'] = 100
#2.使用Flask提供的render_template函数
return render_template('index1.html', **data)
if __name__ == '__main__':
app.run(port=8000)
在项目目录下创建static
目录,index1.html:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{title}}title>
head>
<body>
<h1>{{num}}h1>
<a href='/show_info'>信息展示a>
<a href='/show_contain'>内置变量展示a>
<a href='/filter'>过滤器a>
<a href='/jicheng'>继承a>
body>
html>
Jinja2 模版中的变量代码块可以是任意 Python 类型或者对象,只要它能够被 Python 的 __str__
方法或者str()转换为一个字符串就可以。
main.py:
from flask import Flask, render_template, session, g
app = Flask(__name__,
template_folder='templates', # html页面
static_folder='static', # 静态资源
static_url_path='/static' # 提供给外界的访问路径
)
# 各种数据类型的模板渲染
@app.route('/show_info')
def show_info():
data = {}
data['info'] = {
'name': 'xyw',
'age': 18,
'sex': True,
}
data['student_list'] = ['xyw1', 'xyw2', 'xyw3', 'xyw4', 'xyw5']
data['goods_list'] = [
{"id": 10, "name": "Python7天入门到放弃", "price": 99.9, "num": 100},
{"id": 11, "name": "Python3天入门到放弃", "price": 99.9, "num": 100},
{"id": 12, "name": "Python5天入门到放弃", "price": 99.9, "num": 100},
{"id": 13, "name": "Go7天入门到放弃", "price": 99.9, "num": 100},
{"id": 14, "name": "Go5天入门到放弃", "price": 99.9, "num": 100},
{"id": 15, "name": "Linux7天入门到放弃", "price": 99.9, "num": 100},
]
return render_template('index2.html', **data)
if __name__ == '__main__':
app.run(port=8000)
index2.html:
访问列表中的成员,注意:点不支持负下标
在一个 for 循环块中你可以访问这些特殊的变量:
变量 | 描述 |
---|---|
loop.index | 当前循环迭代的次数(从 1 开始) |
loop.index0 | 当前循环迭代的次数(从 0 开始) |
loop.revindex | 到循环结束需要迭代的次数(从 1 开始) |
loop.revindex0 | 到循环结束需要迭代的次数(从 0 开始) |
loop.first | 如果是第一次迭代,为 True 。 |
loop.last | 如果是最后一次迭代,为 True 。 |
loop.length | 序列中的项目数。 |
loop.cycle | 在一串序列间期取值的辅助函数。见下面示例程序。 |
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<h1>访问字典中的成员h1>
<p>{{ info.name }}p>
<p>{{ info['age'] }}p>
<h1>访问列表中的成员,注意:点不支持负下标h1>
<p>{{ student_list.0 }}p>
<p>{{ student_list.2 }}p>
<p>{{ student_list[-1] }}p>
<p>{{ student_list[2] }}p>
<h1>if判断h1>
{% if info.age < 18 %}
<p>小明请出去p>
{% endif %}
{% if info.age > 10 %}
<p>小明同学p>
{% else %}
<p>小明小朋友p>
{% endif %}
{% if info.age < 10 %}
<p>小明小朋友p>
{% elif info.age < 18 %}
<p>小明同学p>
{% else %}
<p>大明同学p>
{% endif %}
<h1>for循环,通常和if判断配合使用h1>
<ul>
{% for student in student_list %}
{% if loop.last %}
<li style="background-color: #000;color:#fff">{{ student }}li>
{% else %}
<li>{{ student }}li>
{% endif %}
{% endfor %}
ul>
<table>
<tr>
<th>序号th>
<th>IDth>
<th>名称th>
<th>价格th>
<th>库存th>
tr>
{% for goods in goods_list %}
{% if loop.index % 2 %}
<tr bgcolor="#faebd7">
{% else %}
<tr>
{% endif %}
<td>{{ loop.index }}td>
<td>{{ goods.id }}td>
<td>{{ goods.name }}td>
<td>{{ goods.price }}td>
<td>{{ goods.num }}td>
tr>
{% endfor %}
table>
body>
html>
可以在模板中访问一些 Flask 默认内置的函数和对象:
config
/request
/session
/g变量
/url_for()
main.py:
from flask import Flask, render_template, session, g
app = Flask(__name__,
template_folder='templates', # html页面
static_folder='static', # 静态资源
static_url_path='/static' # 提供给外界的访问路径
)
# 内置变量
app.config['SECRET_KEY'] = '123456'
@app.route('/show_contain')
def show_contain():
session['name'] = 'xyw'
g.name = 'xyw_g'
return render_template('index3.html', )
if __name__ == '__main__':
app.run(port=8000)
index3.html:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<h1>requesth1>
<p>{{ request.url }}p> {# http://127.0.0.1:8000/show_contain #}
<p>{{ request.method }}p>
<h1>sessionh1>
<p>{{ session }}p>
<p>{{ session.name }}p>
<h1>configh1>
<p>{{ config.DEBUG }}p>
<h1>url_forh1>
<p>{{ url_for('show_contain') }}p> {# /show_contain #}
<p>{{ url_for('show_info') }}p> {# /show_info #}
<h1>g变量h1>
<p>{{ g.name }}p>
body>
html>
字符串操作:
safe:禁用转义
capitalize:把变量值的首字母转成大写,其余字母转小写
lower:把值转成小写
upper:把值转成大写
title:把值中的每个单词的首字母都转成大写
reverse:字符串反转
format:格式化输出
striptags:渲染之前把值中所有的HTML标签都删掉
truncate: 字符串截断
列表操作:
first:取第一个元素
last:取最后一个元素
length:获取列表长度
sum:列表求和
sort:列表排序
语句块过滤:
{% filter upper %}
#一大堆文字#
{% endfilter %}
例子:
main.py:
from flask import Flask, render_template
app = Flask(__name__,
template_folder='templates', # html页面
static_folder='static', # 静态资源
static_url_path='/static' # 提供给外界的访问路径
)
# 过滤器
@app.route('/filter')
def show_filter():
data = {}
data['message'] = 'hello,world'
data['image'] = ""
data['mobile'] = '13813241123'
return render_template('index4.html', **data)
if __name__ == '__main__':
app.run(port=8000)
index4.html:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<p>{{ message | upper }}p>
<p>{{ image | safe }}p>
<p>{{ message | reverse | upper }}p>
<p>{{ '<em>helloem>' | striptags }}p>
<p>{{ "如果x<y,z>x,那么x和z之间是否相等?" | striptags }}p>
<p>{{ '床前明月光,疑是地上霜。' | truncate(5,False,'...', 0)}}p>
<p>{{ [1,1,2,3,4,5,1,2,2,3,4] | unique | list }}p>
body>
html>
1.自定义一个方法
2.使用add_template_filter(方法名,过滤器名)
注册
例(网页上隐藏手机号):
main.py:
from flask import Flask, render_template
app = Flask(__name__,
template_folder='templates', # html页面
static_folder='static', # 静态资源
static_url_path='/static' # 提供给外界的访问路径
)
@app.route('/filter')
def show_filter():
data = {}
data['mobile'] = '13813241123'
return render_template('index4.html', **data)
# 自定义过滤器
# 1.自定义一个方法
def do_mobile(content):
return content[:3]+'*****'+content[-3:]
# 2.使用add_template_filter注册
app.add_template_filter(do_mobile, 'mobile')
index4.html:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<p>{{ mobile | mobile }}p>
body>
html>
与django使用方法一样
1.在母版中,遇到需要定制的地方使用{% block 名字%}{endblock}
标记
2.在子版中,先引入母版{% extends 'base.html' %}
,再在{% block 名字%}{endblock}
中写内容
3.需要用到母版中的内容,直接用{{ super() }}
即可
模板继承使用时注意点:
from flask import Flask, render_template
app = Flask(__name__,
template_folder='templates', # html页面
static_folder='static', # 静态资源
static_url_path='/static' # 提供给外界的访问路径
)
# 模板继承
@app.route('/jicheng')
def jicheng():
return render_template('index5.html')
if __name__ == '__main__':
app.run(port=8000)
index5.html:
{% extends 'base.html' %}
{% block title %}子模板的标题{% endblock %}
{% block hander%}
<script>
alert('xxx')
script>
{% endblock %}
{% block content %}
{{ super() }}
<p>子模板的内容p>
{{ super() }}
{% endblock %}
base.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}父级模板的标题{% endblock %}title>
{% block hander %}{% endblock %}
head>
<body>
{% block content %}
<p>父模板的内容p>
{% endblock %}
body>
html>
一个典型的CSRF攻击有着如下的流程:
1.受害者登录a.com,并保留了登录凭证(Cookie)。
2.攻击者引诱受害者访问了b.com。
3.b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会默认携带a.com的Cookie。
4.a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。
5.a.com以受害者的名义执行了act=xx。
6.攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作。
前后端分离:
随着前后端分离与单页应用的到来,我们往往在后端使用 RESTful 的方式暴露接口,前端使用 react、angular 或者 VUE 来控制渲染和交互,那么,也就不存在如何在 form 中放入一个 token 来进行 CSRF 的验证了。对于 RESTful 的接口,本质上是无状态的(stateless),而 anti-CSRF token 是依靠 session 中的状态来进行判断,那么也就无法再使用这种方式了。
在前后端进行分离后,最简单的集成方式:
1)用户通过浏览器请求某个网站例如 www.google.com,然后 DNS 转移至前端站点,获取前端资源
2)返回页面,JS,CSS 等后,浏览器进行渲染页面,这时候用户就能看到页面了
3)在页面准备好后,用户的所有操作(不论是 form 提交、还是 ajax 请求),都发送给后端服务,再通过 web service 响应,修改页面,支持业务逻辑
这个流程中,对于真正存储、修改用户数据的后端服务,是无状态的,而用户所操作的 form 是完全由前端应用控制,后端服务无法感知。所以,即使前端使用某种方式在 form 中放入了 token,但是后端也无法验证,这种 anti CSRF token 的方式是无法实现的。
资料1
资料2
在 Flask 中, Flask-wtf 扩展有一套完善的 csrf 防护体系
安装flask_wtf
pip install flask_wtf
1.导入 flask_wtf.csrf 中的 CSRFProtect 类,进行初始化,并在初始化的时候关联 app
2.设置应用程序的 secret_key,用于加密生成的 csrf_token 的值
3.前端提交form表单时,携带
main.py:
from flask import Flask, render_template, request
# 1. 导入 flask_wtf.csrf 中的 CSRFProtect 类,进行初始化,并在初始化的时候关联 app
from flask_wtf import CSRFProtect
app = Flask(__name__, template_folder='templates')
csrf = CSRFProtect(app)
# 2. 设置应用程序的 secret_key,用于加密生成的 csrf_token 的值
app.config['SECRET_KEY'] = '123QAZ'
@app.route('/')
def index():
return render_template('index6.html')
@app.route('/login',methods=['POST'])
def login():
print(request.form)
'''
ImmutableMultiDict([
('csrf_token', 'IjBjMmY1NDcyZTYxYzk0MDg0ZDk5NmNmODAwNmU2YTkxYzJjMTJhMDEi.X7fChw.VQoiibCR2vCnzmvFmNiMaKLIGlI'),
('username', 'xyw'),
('password', '111')
])
'''
return 'ok'
if __name__ == '__main__':
app.run(port=8000)
index6.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form action="{{ url_for('login') }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
帐号:<input type="text" name="username" value="">
密码:<input type="password" name="password" value="">
<input type="submit" value="登录">
form>
body>
html>
ORM 全拼Object-Relation Mapping
,中文意为 对象-关系映射。主要实现模型对象到关系数据库数据的映射
优点 :
sql语句
.缺点 :
flask默认提供模型操作,但是并没有提供ORM,所以一般开发的时候我们会采用flask-SQLAlchemy模块来实现ORM操作。
SQLAlchemy是一个关系型数据库框架,它提供了高层的 ORM 和底层的原生数据库的操作。flask-sqlalchemy 是一个简化了 SQLAlchemy 操作的flask扩展。
SQLAlchemy: https://www.sqlalchemy.org/
中文文档: https://www.osgeo.cn/sqlalchemy/index.html
安装flask-SQLAlchemy模块
pip install flask-sqlalchemy -i https://pypi.tuna.tsinghua.edu.cn/simple
如果连接的是 mysql 数据库,需要安装 mysqldb 驱动
pip install flask-mysqldb -i https://pypi.tuna.tsinghua.edu.cn/simple
安装flask-mysqldb时,注意
安装 flask-mysqldb的时候,python底层依赖于一个底层的模块 mysql-client模块
如果没有这个模块,则会报错如下:
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-install-21hysnd4/mysqlclient/
解决方案:
sudo apt-get install libmysqlclient-dev python3-dev
运行上面的安装命令如果再次报错如下:
dpkg 被中断,您必须手工运行 ‘sudo dpkg --configure -a’ 解决此问题。
则根据提示执行命令以下命令,再次安装mysqlclient
sudo dpkg --configure -a
apt-get install libmysqlclient-dev python3-dev
解决了mysqlclient问题以后,重新安装 flask-mysqldb即可。
pip install flask-mysqldb -i https://pypi.tuna.tsinghua.edu.cn/simple
连接数据库:
1.配置数据库参数
2.初始化SQLAlchemy,链接数据库
创建表:
1. 创建模型类
2. 根据模型创建所有的数据表(在`with app.app_context()`中`db.create_all()`)
常用的SQLAIchemy字段类型:
模型字段类型名 | python中数据类型 | 说明 |
---|---|---|
Integer | int | 普通整数,一般是32位 |
SmallInteger | int | 取值范围小的整数,一般是16位 |
BigInteger | int或long | 不限制精度的整数 |
Float | float | 浮点数 |
Numeric | decimal.Decimal | 普通数值,一般是32位 |
String | str | 变长字符串 |
Text | str | 变长字符串,对较长或不限长度的字符串做了优化 |
Unicode | unicode | 变长Unicode字符串 |
UnicodeText | unicode | 变长Unicode字符串,对较长或不限长度的字符串做了优化 |
Boolean | bool | 布尔值 |
Date | datetime.date | 日期 |
Time | datetime.time | 时间 |
DateTime | datetime.datetime | 日期和时间 |
LargeBinary | str | 二进制文件内容 |
常用的SQLAIchemy列约束选项:
选项名 | 说明 |
---|---|
primary_key | 如果为True,代表表的主键 |
unique | 如果为True,代表这列不允许出现重复的值 |
index | 如果为True,为这列创建索引,提高查询效率 |
nullable | 如果为True,允许有空值,如果为False,不允许有空值 |
default | 为这列定义默认值 |
from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# 1.配置数据库参数
class Config():
# SQLALCHEMY_DATABASE_URI = "mysql://账号:密码@IP/数据库名?编码"
SQLALCHEMY_DATABASE_URI = "mysql://root:[email protected]:3306/students?charset=utf8mb4"
# 动态追踪修改设置,如未设置只会提示警告
SQLALCHEMY_TRACK_MODIFICATIONS = True
# 查询时会显示原始SQL语句
SQLALCHEMY_ECHO = True
app.config.from_object(Config)
# 2.初始化SQLAlchemy,链接数据库
db = SQLAlchemy() # 初始化数据库操作对象
db.init_app(app) # 初始化数据库链接
"""创建模型类"""
# 1. 创建模型类
class Student(db.Model):
__tablename__ = "tb_student"
id = db.Column(db.Integer, primary_key=True,comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
age = db.Column(db.Integer, comment="年龄")
sex = db.Column(db.Boolean, default=False, comment="性别")
money = db.Column(db.DECIMAL(8,2), nullable=True, comment="钱包")
def __repr__(self):
return self.name
class Teacher(db.Model):
__tablename__ = "tb_teacher"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
sex = db.Column(db.Boolean, default=False, comment="性别")
option = db.Column(db.Enum("讲师","助教","班主任"), default="讲师", comment="教职")
def __repr__(self):
return self.name
class Course(db.Model):
__tablename__ = "tb_course"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), unique=True, comment="课程名称")
price = db.Column(db.Numeric(6, 2))
def __repr__(self):
return self.name
@app.route('/')
def index():
return 'ok'
if __name__ == '__main__':
# 2.根据模型创建所有的数据表
with app.app_context():
db.create_all()# 注意,create_all()方法执行的时候,需要放在模型的后面
# 上面这段语句,后面我们需要转移代码到flask-script的自定义命令中。
# 执行了一次以后,需要注释掉。
#删除表:db.drop_all()
app.run()
1.增加一条数据add
1)用db.session调用add方法 2)需要提交事物
2.批量增加数据 add_all()
(也需要提交事物)其他方法
@app.route('/')
def index():
'''增'''
# 1.增加一条数据 add 1)用db.session调用add方法 2)需要提交事物
student1 = Student(name='xyw', age=16, money=100, sex=True)
db.session.add(student1)
db.session.commit()
# 2.批量增加数据 add_all()
# 其他方法:https://blog.csdn.net/weixin_44777680/article/details/105654220
data_list = [
Student(name="xiaohui1号",age=16,money=1000, sex=True),
Student(name="xiaohui2号",age=16,money=1000, sex=True),
Student(name="xiaohui3号",age=16,money=1000, sex=True),
Student(name="xiaohui4号",age=16,money=1000, sex=True),
Student(name="xiaohui5号",age=16,money=1000, sex=True),
Student(name="xiaohui6号",age=16,money=1000, sex=True),
]
db.session.add_all(data_list)
db.session.commit()
return 'ok'
if __name__ == '__main__':
app.run()
1.get 不会报错
2.filter 写布尔值模型.query.filter(模型.字段==条件值).first()
3.加.first()或all()
得到的类型:
;不加是是BaseQuery
类型。所以更新和修改有两种方式:1.先查后改 2.直接根据条件改(乐观锁)
@app.route('/')
def index():
# 1.get (不会报错)
# 根据主键ID查询一条数据,如果ID不存在,则返回None不会报错!
student = Student.query.get(100)
if student is None:
print("当前学生不存在!")
else:
print(student)
print(student.name,student.age) # 获取属性
# 2.filter (写布尔值)
# 根据查询条件获取一条数据
# 模型.query.filter(模型.字段==条件值).first()
student = Student.query.filter(Student.id==1)
print('------------',student, type(student))
'''
打印结果:
(1)加.first():
(2)不加(是BaseQuery类型):
SELECT
tb_student.id AS tb_student_id, tb_student.name AS tb_student_name, tb_student.age AS tb_student_age, tb_student.sex AS tb_student_sex, tb_student.money AS tb_student_money
FROM
tb_student
WHERE
tb_student.id = %s
所以:
更新和修改有两种方式:1.先查后改 2.直接根据条件改(乐观锁)
'''
# print(student.name,student.money)
# 根据查询条件获取多条数据
# 模型.query.filter(模型.字段==条件值).all()
student_list = Student.query.filter(Student.id < 5).all()
print(student_list, type(student_list))
# """打印效果;
# [xiaoming, xiaohong, xiaohui1号, xiaohui2号]
# """
for student in student_list:
print(student.name, student.money)
return 'ok'
if __name__ == '__main__':
app.run()
1.有两种方式:1)先查后改;2)乐观锁
2.可以实现django中F函数的效果
@app.route('/')
def index():
'''改'''
# 方式1
Student.query.filter(Student.name == "xyw").first().money += 999
db.session.commit()
# 方式2
Student.query.filter(Student.name == "xyw").update({Student.sex: True})
db.session.commit()
# 实现F函数效果
Student.query.filter(Student.name == "xyw").update({Student.money: Student.money+555})
db.session.commit()
return 'ok'
@app.route('/')
def index():
# 方式1
db.session.delete( Student.query.filter(Student.name == "xiaohui6号").first() )
# 方式2
Student.query.filter(Student.name == "xiaohui5号").delete()
db.session.commit()
return 'ok'
student = Student.query.filter(Student.money != 1000.00).all()
print(student)
"""
模型.字段.like("%值%") 等价于 模型.字段.contains("值") 包含xxx
模型.字段.like("值%") 等价于 模型.字段.startswith("值") 以xxx开头
模型.字段.like("%值") 等价于 模型.字段.endswith("值") 以xxx结尾
模型.字段.like("__") 值长度为2个字符的.几个下划线代表几个字符
"""
student1 = Student.query.filter(Student.name.like('x%')).all()
student2 = Student.query.filter(Student.name.endswith('w')).all()
student3 = Student.query.filter(Student.name.like('___')).all()
print(student1, student2, student3)
student = Student.query.filter_by(money=1000).all()
print(student)
from sqlalchemy import and_, or_, not_
student1 = Student.query.filter(and_(Student.money == 1000, Student.name.like('x%'))).all()
student2 = Student.query.filter(or_(Student.money == 3208.00, Student.age < 18)).all()
student3 = Student.query.filter(not_(Student.age != 188)).all()
print(student1, student2, student3)
student = Student.query.filter(Student.age.in_([16, 18, 188])).all()
print(student)
"""
order_by(模型.字段.desc())或db.desc(模型.字段) 倒序
order_by(模型.字段.asc())或db.asc(模型.字段) 升序
"""
student_list1 = Student.query.order_by(Student.age).all()
student_list2 = Student.query.order_by(Student.age.desc()).all()
student_list3 = Student.query.order_by(db.asc(Student.age)).all()
print(student_list1, student_list2, student_list3)
student_count = Student.query.filter(Student.age > 17).count()
print(student_count) # 4
student_list = Student.query.order_by(Student.age).offset(2).limit(3).all()
print(student_list1, student_list)
'''
从索引为2的位置开始取,取3个
[xiaohui1号, xiaohui2号, xiaohui3号, xiaohui4号, xyw]
[xiaohui3号, xiaohui4号, xyw]
'''
'''
paginate(page=当前页码, per_page=每一页数据量, max_per_page=每一页最大数据量)
1)当前页码,默认是从request.args["page"],如果当前参数没有值,则默认为1
2)每一页数据量,默认是100条
3)因为分页器有提供了一个 request.args.["per_page"]给客户端设置每一页数据量,
所以再次限定客户端最多能设置的每一页数据量
'''
pagination = Student.query.filter(Student.sex==True).paginate(per_page=1)
print('----', pagination)
print(pagination.items) # 获取当前页数据量
print(pagination.has_next) # 如果还有下一页数据,则结果为True
print(pagination.has_prev) # 如果有上一页数据,则结果为True
print(pagination.page) # 当前页页码 request.args.get("page",1)
print(pagination.total) # 本次查询结果的数据总量[被分页的数据量总数]
print(pagination.pages) # 总页码
print('------', pagination.prev()) # 上一页的分页器对象,如果没有上一页,则默认为None
'''
------
'''
print(pagination.next()) # 下一页的分页器对象,如果没有下一页,则默认为None
if pagination.has_next:
print(pagination.next().items) # 下一页的数据列表
# return render_template('list.html', pagination=pagination)
from sqlalchemy import func
# 查询男生和女生的最大年龄
ret = db.session.query(Student.sex, func.max(Student.age)).group_by(Student.sex).all()
print(ret)
# 查询男生和女生人数
ret1 = db.session.query(Student.sex, func.count(Student.sex)).group_by(Student.sex).all()
print(ret1)
"""
db.session.execute('原生sql语句').fetchall()/fetchone()
"""
ret = db.session.execute('select id,name,age,if(sex,"男","女") from tb_student').fetchall()
ret = db.session.execute("select * from tb_student where id = 3").fetchone()
print(ret)
# 添加/修改/删除
# db.session.execute("UPDATE tb_student SET money=(money + %s) WHERE age = %s" % (200, 22))
# db.session.commit()
# 查询出女生和男生中大于18岁的人数
ret = db.session.execute(
"SELECT IF(sex,'男','女'), count(id) from (SELECT id,name,age,sex FROM `tb_student` WHERE age>18) as stu group by sex"
).fetchall()
print(ret)
完整代码:
mian.py:
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
class Config():
DEBUG = True
# 数据库链接配置
SQLALCHEMY_DATABASE_URI = 'mysql://root:[email protected]:3306/students?charset=utf8mb4'
# 动态追踪修改设置,如未设置只会提示警告
SQLALCHEMY_TRACK_MODIFICATIONS = True
# 查询时显示原始sql语句
SQLALCHEMY_ECHO = True
# 加载配置
app.config.from_object(Config)
# 实例化数据库,关联
db = SQLAlchemy()
db.init_app(app)
"""创建模型类"""
class Student(db.Model):
__tablename__ = "tb_student"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
age = db.Column(db.Integer, comment="年龄")
sex = db.Column(db.Boolean, default=False, comment="性别")
money = db.Column(db.DECIMAL(8, 2), nullable=True, comment="钱包")
def __repr__(self):
return self.name
class Teacher(db.Model):
__tablename__ = "tb_teacher"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
sex = db.Column(db.Boolean, default=False, comment="性别")
option = db.Column(db.Enum("讲师", "助教", "班主任"), default="讲师", comment="教职")
def __repr__(self):
return self.name
class Course(db.Model):
__tablename__ = "tb_course"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), unique=True, comment="课程名称")
price = db.Column(db.Numeric(6, 2))
def __repr__(self):
return self.name
@app.route('/')
def index():
"""1.filter支持的判断条件:== <= >= < > !="""
student = Student.query.filter(Student.money != 1000.00).all()
print(student)
"""2.filter的模糊查询
模型.字段.like("%值%") 等价于 模型.字段.contains("值") 包含xxx
模型.字段.like("值%") 等价于 模型.字段.startswith("值") 以xxx开头
模型.字段.like("%值") 等价于 模型.字段.endswith("值") 以xxx结尾
模型.字段.like("__") 值长度为2个字符的.几个下划线代表几个字符
"""
student1 = Student.query.filter(Student.name.like('x%')).all()
student2 = Student.query.filter(Student.name.endswith('w')).all()
student3 = Student.query.filter(Student.name.like('___')).all()
print(student1, student2, student3)
"""3.filter_by 设置精确条件查找(不用声明模型类名)"""
student = Student.query.filter_by(money=1000).all()
print(student)
"""4.filter多条件查询:and_ or_ not_"""
from sqlalchemy import and_, or_, not_
student1 = Student.query.filter(and_(Student.money == 1000, Student.name.like('x%'))).all()
student2 = Student.query.filter(or_(Student.money == 3208.00, Student.age < 18)).all()
student3 = Student.query.filter(not_(Student.age != 188)).all()
print(student1, student2, student3)
"""5.filter范围查询 模型.字段.in_([])"""
student = Student.query.filter(Student.age.in_([16, 18, 188])).all()
print(student)
"""6.order_by结果排序 (默认是升序)
order_by(模型.字段.desc())或db.desc(模型.字段) 倒序
order_by(模型.字段.asc())或db.asc(模型.字段) 升序
"""
student_list1 = Student.query.order_by(Student.age).all()
student_list2 = Student.query.order_by(Student.age.desc()).all()
student_list3 = Student.query.order_by(db.asc(Student.age)).all()
print(student_list1, student_list2, student_list3)
"""7.count 统计结果"""
student_count = Student.query.filter(Student.age > 17).count()
print(student_count) # 4
"""8.limit结果数量进行限制;offset指定查询的开始位置"""
student_list = Student.query.order_by(Student.age).offset(2).limit(3).all()
print(student_list1, student_list)
'''
从索引为2的位置开始取,取3个
[xiaohui1号, xiaohui2号, xiaohui3号, xiaohui4号, xyw]
[xiaohui3号, xiaohui4号, xyw]
'''
"""9.paginate 分页器"""
'''
paginate(page=当前页码, per_page=每一页数据量, max_per_page=每一页最大数据量)
1)当前页码,默认是从request.args["page"],如果当前参数没有值,则默认为1
2)每一页数据量,默认是100条
3)因为分页器有提供了一个 request.args.["per_page"]给客户端设置每一页数据量,
所以再次限定客户端最多能设置的每一页数据量
'''
pagination = Student.query.filter(Student.sex==True).paginate(per_page=1)
print('----', pagination)
print(pagination.items) # 获取当前页数据量
print(pagination.has_next) # 如果还有下一页数据,则结果为True
print(pagination.has_prev) # 如果有上一页数据,则结果为True
print(pagination.page) # 当前页页码 request.args.get("page",1)
print(pagination.total) # 本次查询结果的数据总量[被分页的数据量总数]
print(pagination.pages) # 总页码
print('------', pagination.prev()) # 上一页的分页器对象,如果没有上一页,则默认为None
'''
------
'''
print(pagination.next()) # 下一页的分页器对象,如果没有下一页,则默认为None
if pagination.has_next:
print(pagination.next().items) # 下一页的数据列表
# return render_template('list.html', pagination=pagination)
"""10.group_by 分组查询(func.max(), func.count(), having())"""
from sqlalchemy import func
# 查询男生和女生的最大年龄
ret = db.session.query(Student.sex, func.max(Student.age)).group_by(Student.sex).all()
print(ret)
# 查询男生和女生人数
ret1 = db.session.query(Student.sex, func.count(Student.sex)).group_by(Student.sex).all()
print(ret1)
"""11.执行原生sql语句(返回结果不是模型对象, 是列表和元祖)
db.session.execute('原生sql语句').fetchall()/fetchone()
"""
ret = db.session.execute('select id,name,age,if(sex,"男","女") from tb_student').fetchall()
ret = db.session.execute("select * from tb_student where id = 3").fetchone()
print(ret)
# 添加/修改/删除
# db.session.execute("UPDATE tb_student SET money=(money + %s) WHERE age = %s" % (200, 22))
# db.session.commit()
# 查询出女生和男生中大于18岁的人数
ret = db.session.execute(
"SELECT IF(sex,'男','女'), count(id) from (SELECT id,name,age,sex FROM `tb_student` WHERE age>18) as stu group by sex"
).fetchall()
print(ret)
return 'ok'
if __name__ == '__main__':
# with app.app_context():
# db.drop_all()
# db.create_all()
app.run()
list.html:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<style>
.page a,.page span{
padding: 2px 6px;
color: #fff;
background: #6666ff;
text-decoration: none;
}
.page span{
color: #fff;
background: orange;
}
style>
head>
<body>
<table border="1" align="center" width="600">
<tr>
<th>IDth>
<th>ageth>
<th>nameth>
<th>sexth>
<th>moneyth>
tr>
{% for student in pagination.items %}
<tr>
<td>{{ student.id }}td>
<td>{{ student.age }}td>
<td>{{ student.name }}td>
<td>{{ "男" if student.sex else "女" }}td>
<td>{{ student.money }}td>
tr>
{% endfor %}
<tr align="center">
<td colspan="5" class="page">
{% if pagination.has_prev %}
首 页a>
<a href="?page={{ pagination.prev_num }}">上一页a>
<a href="?page={{ pagination.prev_num }}">{{ pagination.prev_num }}a>
{% endif %}
<span>{{ pagination.page }}span>
{% if pagination.has_next %}
<a href="?page={{ pagination.next_num }}">{{ pagination.next_num }}a>
<a href="?page={{ pagination.next_num }}">下一页a>
<a href="?page={{ pagination.pages }}">尾 页a>
{% endif %}
td>
tr>
table>
body>
html>
1.创建一对一对象属性(学生表)
1.关联属性,是SQLAlchemy提供给开发者快速引用外键模型的一个对象属性,不存在于mySQL中
2.backref 反向引用,类似django的related,通过外键模型查询主模型数据时的关联属性
3.uselist=False表示关联一个数据
2.外键(学生详情信息表)
如果是一对一,则外键放在附加表对应的模型中
如果是一对多,则外键放在多的表对象的模型中
"""创建模型类"""
class Student(db.Model):
__tablename__ = "tb_student"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
age = db.Column(db.Integer, comment="年龄")
sex = db.Column(db.Boolean, default=False, comment="性别")
money = db.Column(db.DECIMAL(8, 2), nullable=True, comment="钱包")
"""创建一对一
1.关联属性,是SQLAlchemy提供给开发者快速引用外键模型的一个对象属性,不存在于mySQL中
2.backref 反向引用,类似django的related,通过外键模型查询主模型数据时的关联属性
3.uselist=False表示关联一个数据
"""
info = db.relationship('StudentInfo', backref='own', uselist=False)
def __repr__(self):
return self.name
class StudentInfo(db.Model):
__tablename__ = 'tb_student_info'
id = db.Column(db.Integer, primary_key=True, comment='主键id')
'''外键
如果是一对一,则外键放在附加表对应的模型中
如果是一对多,则外键放在多的表对象的模型中
'''
sid = db.Column(db.Integer, db.ForeignKey(Student.id), comment='学生')
address = db.Column(db.String(255), nullable=True, comment="家庭住址")
mobile = db.Column(db.String(15), unique=True, comment="紧急联系电话")
def __repr__(self):
return self.own.name
class Teacher(db.Model):
__tablename__ = "tb_teacher"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
sex = db.Column(db.Boolean, default=False, comment="性别")
option = db.Column(db.Enum("讲师", "助教", "班主任"), default="讲师", comment="教职")
def __repr__(self):
return self.name
class Course(db.Model):
__tablename__ = "tb_course"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), unique=True, comment="课程名称")
price = db.Column(db.Numeric(6, 2))
def __repr__(self):
return self.name
@app.route('/')
def index():
"""添加数据"""
student = Student(
name='徐彦伟',
age=13,
sex=True,
money=1000,
info=StudentInfo(
mobile='1383838438',
address='北京市昌平区白沙路103号'
)
)
db.session.add(student)
db.session.commit()
"""查"""
# 查 xyw 的电话
mobile = Student.query.first().info.mobile
print(mobile)
# 查电话为1383838438的同学名称(反向查询)
student = StudentInfo.query.filter(StudentInfo.mobile=='1383838438').first().own.name
print(student)
'''改'''
student = Student.query.get(1)
student.age = 188
student.info.address = '北京市昌平区沙河镇白沙路130号'
db.session.commit()
Student.query.filter(Student.name == '徐彦伟').update({Student.age: 180})
Student.query.filter(Student.name == '徐彦伟').first().info.address = '北京市昌平区沙河镇白沙路133号'
db.session.commit()
'''删'''
student = Student.query.get(2)
db.session.delete(student.info) # 先删除外键模型,再删主模型
db.session.delete(student)
db.session.commit()
return 'ok'
完整代码:
main.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
class Config():
# DEBUG调试模式
DEBUG = True
# 数据库链接配置
# SQLALCHEMY_DATABASE_URI = "mysql://账号:密码@IP/数据库名?编码"
SQLALCHEMY_DATABASE_URI = "mysql://root:[email protected]:3306/students?charset=utf8mb4"
# 动态追踪修改设置,如未设置只会提示警告
SQLALCHEMY_TRACK_MODIFICATIONS = True
# 查询时会显示原始SQL语句
SQLALCHEMY_ECHO = True
app.config.from_object(Config)
db = SQLAlchemy()
db.init_app(app)
"""创建模型类"""
class Student(db.Model):
__tablename__ = "tb_student"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
age = db.Column(db.Integer, comment="年龄")
sex = db.Column(db.Boolean, default=False, comment="性别")
money = db.Column(db.DECIMAL(8, 2), nullable=True, comment="钱包")
"""创建一对一
1.关联属性,是SQLAlchemy提供给开发者快速引用外键模型的一个对象属性,不存在于mySQL中
2.backref 反向引用,类似django的related,通过外键模型查询主模型数据时的关联属性
3.uselist=False表示关联一个数据
"""
info = db.relationship('StudentInfo', backref='own', uselist=False)
def __repr__(self):
return self.name
class StudentInfo(db.Model):
__tablename__ = 'tb_student_info'
id = db.Column(db.Integer, primary_key=True, comment='主键id')
'''外键
如果是一对一,则外键放在附加表对应的模型中
如果是一对多,则外键放在多的表对象的模型中
'''
sid = db.Column(db.Integer, db.ForeignKey(Student.id), comment='学生')
address = db.Column(db.String(255), nullable=True, comment="家庭住址")
mobile = db.Column(db.String(15), unique=True, comment="紧急联系电话")
def __repr__(self):
return self.own.name
class Teacher(db.Model):
__tablename__ = "tb_teacher"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
sex = db.Column(db.Boolean, default=False, comment="性别")
option = db.Column(db.Enum("讲师", "助教", "班主任"), default="讲师", comment="教职")
def __repr__(self):
return self.name
class Course(db.Model):
__tablename__ = "tb_course"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), unique=True, comment="课程名称")
price = db.Column(db.Numeric(6, 2))
def __repr__(self):
return self.name
@app.route('/')
def index():
"""添加数据"""
# student = Student(
# name='徐彦伟',
# age=13,
# sex=True,
# money=1000,
# info=StudentInfo(
# mobile='1383838438',
# address='北京市昌平区白沙路103号'
# )
# )
# db.session.add(student)
# db.session.commit()
"""查"""
# 查 xyw 的电话
mobile = Student.query.first().info.mobile
print(mobile)
# 查电话为1383838438的同学名称(反向查询)
student = StudentInfo.query.filter(StudentInfo.mobile=='1383838438').first().own.name
print(student)
'''改'''
# student = Student.query.get(1)
# student.age = 188
# student.info.address = '北京市昌平区沙河镇白沙路130号'
# db.session.commit()
#
# Student.query.filter(Student.name == '徐彦伟').update({Student.age: 180})
# Student.query.filter(Student.name == '徐彦伟').first().info.address = '北京市昌平区沙河镇白沙路133号'
# db.session.commit()
'''删'''
# student = Student.query.get(2)
# db.session.delete(student.info) # 先删除外键模型,再删主模型
# db.session.delete(student)
# db.session.commit()
return 'ok'
if __name__ == '__main__':
# with app.app_context():
# db.drop_all()
# db.create_all()
app.run()
1.创建一对多关联属性
lazy:(决定了什么时候SQLALchemy从数据库中加载数据)
1.lazy='subquery',
查询当前数据模型时,采用子查询(subquery),把外键模型的属性也瞬间查询出来了
2.lazy=True或lazy='select',
查询当前数据模型时,不会把外键模型的数据查询出来,只有操作到外键关联属性时,才进行连表查询数据[执行SQL]
3.lazy='dynamic',
查询当前数据模型时,不会把外键模型的数据查询出来,只有操作到外键关联属性并操作外键模型具体属性时,才进行连表查询数据[执行SQL]
2.创建外键(一对多)
"""创建模型类"""
class Student(db.Model):
__tablename__ = "tb_student"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
age = db.Column(db.Integer, comment="年龄")
sex = db.Column(db.Boolean, default=False, comment="性别")
money = db.Column(db.DECIMAL(8, 2), nullable=True, comment="钱包")
"""创建一对一关联属性
1.关联属性,是SQLAlchemy提供给开发者快速引用外键模型的一个对象属性,不存在于mySQL中
2.backref 反向引用,类似django的related,通过外键模型查询主模型数据时的关联属性
3.uselist=False表示关联一个数据
"""
info = db.relationship('StudentInfo', backref='own', uselist=False)
def __repr__(self):
return self.name
class StudentInfo(db.Model):
__tablename__ = 'tb_student_info'
id = db.Column(db.Integer, primary_key=True, comment='主键id')
'''外键(一对一)
如果是一对一,则外键放在附加表对应的模型中
如果是一对多,则外键放在多的表对象的模型中
'''
sid = db.Column(db.Integer, db.ForeignKey(Student.id), comment='学生')
address = db.Column(db.String(255), nullable=True, comment="家庭住址")
mobile = db.Column(db.String(15), unique=True, comment="紧急联系电话")
def __repr__(self):
return self.own.name
class Teacher(db.Model):
__tablename__ = "tb_teacher"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
sex = db.Column(db.Boolean, default=False, comment="性别")
option = db.Column(db.Enum("讲师", "助教", "班主任"), default="讲师", comment="教职")
'''创建一对多关联属性
lazy:(决定了什么时候SQLALchemy从数据库中加载数据)
1.lazy='subquery',
查询当前数据模型时,采用子查询(subquery),把外键模型的属性也瞬间查询出来了
2.lazy=True或lazy='select',
查询当前数据模型时,不会把外键模型的数据查询出来,只有操作到外键关联属性时,才进行连表查询数据[执行SQL]
3.lazy='dynamic',
查询当前数据模型时,不会把外键模型的数据查询出来,只有操作到外键关联属性并操作外键模型具体属性时,才进行连表查询数据[执行SQL]
'''
course_list = db.relationship('Course', uselist=True, backref='teacher', lazy='subquery')
def __repr__(self):
return self.name
class Course(db.Model):
__tablename__ = "tb_course"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), unique=True, comment="课程名称")
price = db.Column(db.Numeric(6, 2))
'''创建外键(一对多)'''
teacher_id = db.Column(db.Integer, db.ForeignKey(Teacher.id), comment='老师')
def __repr__(self):
return self.name
@app.route('/')
def index():
"""添加数据"""
# 从teacher表中增加
teacher = Teacher(
name='teacher01',
option='班主任',
course_list=[
Course(name="抓羊", price="9.90"),
Course(name="挨打",price="19.90"),
Course(name="炸房子",price="29.90"),
]
)
db.session.add(teacher)
db.session.commit()
# 从课程表中增加
course = Course(
name='python从入门到入坟',
price='199',
teacher=Teacher(name='teacher02', option='讲师')
)
db.session.add(course)
db.session.commit()
'''查'''
# 查询teacher01教哪些课
course_list = Teacher.query.filter(Teacher.name == 'teacher01').first().course_list
print(course_list, type(course_list))
for course in course_list:
print(course.price, course.name)
# 查询 python从入门到入坟 这个课是哪个老师教(反向查询)
teacher = Course.query.filter(Course.name == 'python从入门到入坟').first().teacher.name
print(teacher)
'''改'''
# 将课程 挨打 改为 打人
Course.query.filter(Course.name == '挨打').first().name = '打人'
db.session.commit()
# 将课程 打人 改为 teacher02 教
Course.query.filter(Course.name == '打人').first().teacher_id = \
Teacher.query.filter(Teacher.name == 'teacher02').first().id
db.session.commit()
'''删除数据'''
# 将 teacher01 删除
teacher = Teacher.query.filter(Teacher.name == 'teacher01').first()
for course in teacher.course_list:
db.session.delete(course)
db.session.delete(teacher)
db.session.commit()
return 'ok'
完整代码:
main.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
class Config():
# DEBUG调试模式
DEBUG = True
# 数据库链接配置
# SQLALCHEMY_DATABASE_URI = "mysql://账号:密码@IP/数据库名?编码"
SQLALCHEMY_DATABASE_URI = "mysql://root:[email protected]:3306/students?charset=utf8mb4"
# 动态追踪修改设置,如未设置只会提示警告
SQLALCHEMY_TRACK_MODIFICATIONS = True
# 查询时会显示原始SQL语句
SQLALCHEMY_ECHO = True
app.config.from_object(Config)
db = SQLAlchemy()
db.init_app(app)
"""创建模型类"""
class Student(db.Model):
__tablename__ = "tb_student"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
age = db.Column(db.Integer, comment="年龄")
sex = db.Column(db.Boolean, default=False, comment="性别")
money = db.Column(db.DECIMAL(8, 2), nullable=True, comment="钱包")
"""创建一对一关联属性
1.关联属性,是SQLAlchemy提供给开发者快速引用外键模型的一个对象属性,不存在于mySQL中
2.backref 反向引用,类似django的related,通过外键模型查询主模型数据时的关联属性
3.uselist=False表示关联一个数据
"""
info = db.relationship('StudentInfo', backref='own', uselist=False)
def __repr__(self):
return self.name
class StudentInfo(db.Model):
__tablename__ = 'tb_student_info'
id = db.Column(db.Integer, primary_key=True, comment='主键id')
'''外键(一对一)
如果是一对一,则外键放在附加表对应的模型中
如果是一对多,则外键放在多的表对象的模型中
'''
sid = db.Column(db.Integer, db.ForeignKey(Student.id), comment='学生')
address = db.Column(db.String(255), nullable=True, comment="家庭住址")
mobile = db.Column(db.String(15), unique=True, comment="紧急联系电话")
def __repr__(self):
return self.own.name
class Teacher(db.Model):
__tablename__ = "tb_teacher"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
sex = db.Column(db.Boolean, default=False, comment="性别")
option = db.Column(db.Enum("讲师", "助教", "班主任"), default="讲师", comment="教职")
'''创建一对多关联属性
lazy:(决定了什么时候SQLALchemy从数据库中加载数据)
1.lazy='subquery',
查询当前数据模型时,采用子查询(subquery),把外键模型的属性也瞬间查询出来了
2.lazy=True或lazy='select',
查询当前数据模型时,不会把外键模型的数据查询出来,只有操作到外键关联属性时,才进行连表查询数据[执行SQL]
3.lazy='dynamic',
查询当前数据模型时,不会把外键模型的数据查询出来,只有操作到外键关联属性并操作外键模型具体属性时,才进行连表查询数据[执行SQL]
'''
course_list = db.relationship('Course', uselist=True, backref='teacher', lazy='subquery')
def __repr__(self):
return self.name
class Course(db.Model):
__tablename__ = "tb_course"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), unique=True, comment="课程名称")
price = db.Column(db.Numeric(6, 2))
'''创建外键(一对多)'''
teacher_id = db.Column(db.Integer, db.ForeignKey(Teacher.id), comment='老师')
def __repr__(self):
return self.name
@app.route('/')
def index():
"""添加数据"""
# 从teacher表中增加
teacher = Teacher(
name='teacher01',
option='班主任',
course_list=[
Course(name="抓羊", price="9.90"),
Course(name="挨打",price="19.90"),
Course(name="炸房子",price="29.90"),
]
)
db.session.add(teacher)
db.session.commit()
# 从课程表中增加
course = Course(
name='python从入门到入坟',
price='199',
teacher=Teacher(name='teacher02', option='讲师')
)
db.session.add(course)
db.session.commit()
'''查'''
# 查询teacher01教哪些课
course_list = Teacher.query.filter(Teacher.name == 'teacher01').first().course_list
print(course_list, type(course_list))
for course in course_list:
print(course.price, course.name)
# 查询 python从入门到入坟 这个课是哪个老师教(反向查询)
teacher = Course.query.filter(Course.name == 'python从入门到入坟').first().teacher.name
print(teacher)
'''改'''
# 将课程 挨打 改为 打人
Course.query.filter(Course.name == '挨打').first().name = '打人'
db.session.commit()
# 将课程 打人 改为 teacher02 教
Course.query.filter(Course.name == '打人').first().teacher_id = \
Teacher.query.filter(Teacher.name == 'teacher02').first().id
db.session.commit()
'''删除数据'''
# 将 teacher01 删除
teacher = Teacher.query.filter(Teacher.name == 'teacher01').first()
for course in teacher.course_list:
db.session.delete(course)
db.session.delete(teacher)
db.session.commit()
return 'ok'
if __name__ == '__main__':
# with app.app_context():
# db.drop_all()
# db.create_all()
app.run()
通常有两种方式:
1.创建多对多(对用属性只能写一个,不然会冲突)
db.Table(
表名,
db.Column("字段名",字段类型,外键声明),
db.Column("字段名",字段类型,外键声明),
)
2.拆解成3个模型,其中tb_achievement作为单独模型存在(常用)(相当于两个多对多)
具体代码:
"""创建模型类"""
"""创建多对多(也可以拆解成3个模型,其中tb_achievement作为单独模型存在--常用)
db.Table(
表名,
db.Column("字段名",字段类型,外键声明),
db.Column("字段名",字段类型,外键声明),
)
"""
'''方式一'''
# achievement = db.Table(
# 'tb_achievement',
# # 相当于两个一对多
# db.Column('student_id', db.Integer, db.ForeignKey('tb_student.id')),
# db.Column("course_id", db.Integer, db.ForeignKey('tb_course.id')),
#
# # 这里的表信息,在主键模型中,仅仅表达的是关联关系,所以中间表的字段,无法通过主模型来获取
# db.Column("created_time", db.DateTime, comment="考试时间"),
# db.Column("score", db.DECIMAL(5, 2), comment="成绩")
# )
'''方式二'''
from datetime import datetime
class Achievement(db.Model):
__tablename__ = "tb_achievement"
id = db.Column(db.Integer, primary_key=True,comment="主键")
student_id = db.Column(db.Integer, db.ForeignKey("tb_student.id"), comment="学生")
course_id = db.Column(db.Integer, db.ForeignKey("tb_course.id"), comment="课程")
score = db.Column(db.DECIMAL(5,2), nullable=True, comment="成绩分数")
created_time = db.Column(db.DateTime, default=datetime.now(), comment="考试时间")
def __repr__(self):
return "[%s],%s进行了一次%s科目考试,成绩:%s" % (self.created_time,self.student.name,self.course.name,self.score)
class Student(db.Model):
__tablename__ = "tb_student"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
age = db.Column(db.Integer, comment="年龄")
sex = db.Column(db.Boolean, default=False, comment="性别")
money = db.Column(db.DECIMAL(8, 2), nullable=True, comment="钱包")
"""创建一对一关联属性
1.关联属性,是SQLAlchemy提供给开发者快速引用外键模型的一个对象属性,不存在于mySQL中
2.backref 反向引用,类似django的related,通过外键模型查询主模型数据时的关联属性
3.uselist=False表示关联一个数据
"""
info = db.relationship('StudentInfo', backref='own', uselist=False)
# course_list = db.relationship("Course", secondary=achievement, backref="student_list", lazy="dynamic")
achievement_list = db.relationship("Achievement", uselist=True, backref="student", lazy="select")
def __repr__(self):
return self.name
class StudentInfo(db.Model):
__tablename__ = 'tb_student_info'
id = db.Column(db.Integer, primary_key=True, comment='主键id')
'''外键(一对一)
如果是一对一,则外键放在附加表对应的模型中
如果是一对多,则外键放在多的表对象的模型中
'''
sid = db.Column(db.Integer, db.ForeignKey(Student.id), comment='学生')
address = db.Column(db.String(255), nullable=True, comment="家庭住址")
mobile = db.Column(db.String(15), unique=True, comment="紧急联系电话")
def __repr__(self):
return self.own.name
class Teacher(db.Model):
__tablename__ = "tb_teacher"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
sex = db.Column(db.Boolean, default=False, comment="性别")
option = db.Column(db.Enum("讲师", "助教", "班主任"), default="讲师", comment="教职")
'''创建一对多关联属性
lazy:(决定了什么时候SQLALchemy从数据库中加载数据)
1.lazy='subquery',
查询当前数据模型时,采用子查询(subquery),把外键模型的属性也瞬间查询出来了
2.lazy=True或lazy='select',
查询当前数据模型时,不会把外键模型的数据查询出来,只有操作到外键关联属性时,才进行连表查询数据[执行SQL]
3.lazy='dynamic',
查询当前数据模型时,不会把外键模型的数据查询出来,只有操作到外键关联属性并操作外键模型具体属性时,才进行连表查询数据[执行SQL]
'''
course_list = db.relationship('Course', uselist=True, backref='teacher', lazy='subquery')
def __repr__(self):
return self.name
class Course(db.Model):
__tablename__ = "tb_course"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), unique=True, comment="课程名称")
price = db.Column(db.Numeric(6, 2))
'''创建外键(一对多)'''
teacher_id = db.Column(db.Integer, db.ForeignKey(Teacher.id), comment='老师')
# student_list = db.relationship("Student", secondary=achievement, backref='course_list', lazy="dynamic")
achievement_list = db.relationship("Achievement", backref="course", uselist=True, lazy="select")
def __repr__(self):
return self.name
@app.route('/')
def index():
"""添加数据"""
# course1 = Course(name="坑爹", price="9.99", teacher=Teacher(name="灰太狼", option="讲师"))
# course2 = Course(name="坑娘", price="9.99", teacher=Teacher(name="灰太狼", option="讲师"))
# course3 = Course(name="和羊做朋友,一起坑爹", price="99.99", teacher=Teacher(name="喜洋洋", option="讲师"))
# student = Student(
# name="xiaohuihui",
# age=5,
# sex=False,
# money=1000,
# info=StudentInfo(
# mobile="13066666666",
# address="狼村1号别墅",
# ),
# course_list = [
# course1,
# course2,
# course3,
# ]
# )
# db.session.add(student)
# db.session.commit()
"""查"""
# 查询 xiaohuihui 同学 选了哪几门课 注意:.all()
# course_list = Student.query.filter(Student.name == 'xiaohuihui').first().course_list.all()
# print(course_list)
# 查询 "和羊做朋友,一起坑爹" 有那些同学选了
"""添加数据"""
# course1 = Course(name="坑爹", price="9.99", teacher=Teacher(name="灰太狼", option="讲师"))
# course2 = Course(name="坑娘", price="9.99", teacher=Teacher(name="灰太狼", option="讲师"))
# course3 = Course(name="和羊做朋友,一起坑爹", price="99.99", teacher=Teacher(name="喜洋洋", option="讲师"))
# student = Student(
# name="xiaohuihui",
# age=5,
# sex=False,
# money=1000,
# info=StudentInfo(
# mobile="13066666666",
# address="狼村1号别墅",
# ),
# achievement_list= [
# Achievement(course=course1,score=100),
# Achievement(course=course2,score=80),
# Achievement(course=course3,score=85),
# ]
# )
# db.session.add(student)
# db.session.commit()
#
# course = Course.query.filter(Course.name=="坑爹").first()
# student = Student.query.filter(Student.name=="xiaohuihui").first()
# achievement = Achievement(
# course=course,
# student=student,
# score=78
# )
# db.session.add(achievement)
# db.session.commit()
achievement_list = Student.query.filter(Student.name=='xiaohuihui').first().achievement_list
for i in achievement_list:
if i.score > 80:
print(i.course.name)
return 'ok'
安装:
pip install flask-migrate
1.使用flask_script启动项目
2.初始化SQLAlchemy,链接数据库
3.初始化Migrate实例
4.在flask_Srcipt中添加一个db命令
这样使用python manage.py db
可以看到所有命令
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
app = Flask(__name__)
class Config():
DEBUG = True
# 数据库链接配置
# SQLALCHEMY_DATABASE_URI = "mysql://账号:密码@IP/数据库名?编码"
SQLALCHEMY_DATABASE_URI = "mysql://root:[email protected]:3306/students?charset=utf8mb4"
# 动态追踪修改设置,如未设置只会提示警告
SQLALCHEMY_TRACK_MODIFICATIONS = True
# 查询时会显示原始SQL语句
SQLALCHEMY_ECHO = True
# 加载配置
app.config.from_object(Config)
# 使用flask_script启动项目
manager = Manager()
manager.app = app
# 初始化SQLAlchemy,链接数据库
db = SQLAlchemy()
db.init_app(app)
# 数据迁移
# 第一个参数是Flask的实例,第二个参数是SQLAlchemy数据库实例
migrate = Migrate(app, db)
# 在flask_Srcipt中添加一个db命令
manager.add_command('db', MigrateCommand)
"""创建模型类"""
from datetime import datetime
class Achievement(db.Model):
__tablename__ = "tb_achievement"
id = db.Column(db.Integer, primary_key=True,comment="主键")
student_id = db.Column(db.Integer, db.ForeignKey("tb_student.id"), comment="学生")
course_id = db.Column(db.Integer, db.ForeignKey("tb_course.id"), comment="课程")
score = db.Column(db.DECIMAL(5,2), nullable=True, comment="成绩分数")
created_time = db.Column(db.DateTime, default=datetime.now(), comment="考试时间")
def __repr__(self):
return "[%s],%s进行了一次%s科目考试,成绩:%s" % (self.created_time,self.student.name,self.course.name,self.score)
class Student(db.Model):
__tablename__ = "tb_student"
id = db.Column(db.Integer, primary_key=True,comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
avatar = db.Column(db.String(250), comment="头像")
age = db.Column(db.Integer, comment="年龄")
sex = db.Column(db.Boolean, default=False, comment="性别")
money = db.Column(db.DECIMAL(8,2), nullable=True, comment="钱包")
achievement_list = db.relationship("Achievement",uselist=True, backref="student", lazy="select")
# backref 反向引用,类似django的related,通过外键模型查询主模型数据时的关联属性
info = db.relationship("StudentInfo", backref="own", uselist=False)
def __repr__(self):
return self.name
class StudentInfo(db.Model):
__tablename__ = "tb_student_info"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
sid= db.Column(db.Integer,db.ForeignKey(Student.id), comment="学生")
address = db.Column(db.String(255), nullable=True, comment="家庭住址")
mobile = db.Column(db.String(15), unique=True, comment="紧急联系电话")
def __repr__(self):
return self.own.name
class Teacher(db.Model):
__tablename__ = "tb_teacher"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
option = db.Column(db.Enum("讲师","助教","班主任"), default="讲师", comment="教职")
course_list = db.relationship("Course",uselist=True, backref="teacher",lazy="subquery")
def __repr__(self):
return self.name
class Course(db.Model):
__tablename__ = "tb_course"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), unique=True, comment="课程名称")
price = db.Column(db.Numeric(6, 2))
teacher_id = db.Column(db.Integer, db.ForeignKey(Teacher.id),comment="老师")
achievement_list = db.relationship("Achievement", backref="course", uselist=True, lazy="select")
def __repr__(self):
return self.name
@app.route('/')
def index():
return 'ok'
if __name__ == '__main__':
manager.run()
1. 初始化数据迁移的目录
python manage.py db init
2. 数据库的数据迁移版本初始化
python manage.py db migrate -m 'initial migration'
3. 升级版本[创建表/创建字段/修改字段]
python manage.py db upgrade
4. 降级版本[删除表/删除字段/恢复字段](默认返回上一个版本)
python manage.py db downgrade
python manage.py db downgrade 版本号 # 返回到指定版本号对应的版本
5.可以根据history命令找到版本号,然后传给downgrade命令
python manage.py db history
输出格式: -> 版本号 (head), initial migration
安装:
pip install flask-Session
1.初始化FlaskRedis
2.初始化Session
3.加载相关配置
4.连接redis
5.连接session
from flask import Flask, session
from flask_redis import FlaskRedis
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
redis = FlaskRedis()
session_store = Session()
db = SQLAlchemy()
class Config():
# DEBUG调试模式
DEBUG = True
# 数据库链接配置
# session秘钥
SECRET_KEY = "*(%#4sxcz(^(#$#8423"
# session存储方式为redis
SESSION_TYPE = "redis"
# session保存数据到redis时启用的链接对象
SESSION_REDIS = redis
# 如果设置session的生命周期是否是会话期, 为True,则关闭浏览器session就失效
SESSION_PERMANENT = True
# 是否对发送到浏览器上session的cookie值进行加密
SESSION_USE_SIGNER = True
# 保存到redis的session数的名称前缀
SESSION_KEY_PREFIX = "session:"
# redis 链接配置 (可以选择库)
REDIS_URL = 'redis://127.0.0.1:6379/1'
app.config.from_object(Config)
# 连接redis
redis.init_app(app)
# 连接session
session_store.init_app(app)
完整测试代码:
from flask import Flask, session
from flask_redis import FlaskRedis
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
redis = FlaskRedis()
session_store = Session()
db = SQLAlchemy()
class Config():
# DEBUG调试模式
DEBUG = True
# 数据库链接配置
# session秘钥
SECRET_KEY = "*(%#4sxcz(^(#$#8423"
# session存储方式为redis
SESSION_TYPE = "redis"
# session保存数据到redis时启用的链接对象
SESSION_REDIS = redis
# 如果设置session的生命周期是否是会话期, 为True,则关闭浏览器session就失效
SESSION_PERMANENT = True
# 是否对发送到浏览器上session的cookie值进行加密
SESSION_USE_SIGNER = True
# 保存到redis的session数的名称前缀
SESSION_KEY_PREFIX = "session:"
# redis 链接配置 (可以选择库)
REDIS_URL = 'redis://127.0.0.1:6379/1'
app.config.from_object(Config)
# 连接redis
redis.init_app(app)
# 连接session
session_store.init_app(app)
@app.route('/')
def index():
session['username'] = 'xyw'
return 'ok'
@app.route('/get_session')
def get_session():
print(session['username'])
return 'ok'
@app.route("/redis1")
def set_redis():
redis.set("username","xiaohuihui")
redis.hset("brother","zhangfei","17")
return "ok"
@app.route('/redis2')
def get_redis():
user = redis.get('username').decode()
print(user)
brother = redis.hget('brother', 'zhangfei').decode()
print(brother)
redis.hdel('brother', 'zhangfei')
return 'ok'
if __name__ == '__main__':
# with app.app_context():
# db.create_all()
app.run()
相关配置:
from flask import Flask, session
from flask_redis import FlaskRedis
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
redis = FlaskRedis()
session_store = Session()
db = SQLAlchemy()
class Config():
# DEBUG调试模式
DEBUG = True
# 数据库链接配置
# session秘钥
SECRET_KEY = "*(%#4sxcz(^(#$#8423"
'''
# session存储方式为redis
SESSION_TYPE = "redis"
# session保存数据到redis时启用的链接对象
SESSION_REDIS = redis
# 如果设置session的生命周期是否是会话期, 为True,则关闭浏览器session就失效
SESSION_PERMANENT = True
# 是否对发送到浏览器上session的cookie值进行加密
SESSION_USE_SIGNER = True
# 保存到redis的session数的名称前缀
SESSION_KEY_PREFIX = "session:"
# redis 链接配置 (可以选择库)
REDIS_URL = 'redis://127.0.0.1:6379/1'
'''
# 数据库保存session
# SQLALCHEMY_DATABASE_URI = "mysql://账号:密码@IP/数据库名?编码"
SQLALCHEMY_DATABASE_URI = "mysql://root:[email protected]:3306/students?charset=utf8mb4"
SESSION_TYPE = 'sqlalchemy' # session类型为sqlalchemy
SESSION_SQLALCHEMY = db # SQLAlchemy对象
SESSION_SQLALCHEMY_TABLE = 'tb_session' # session要保存的表名称
SESSION_PERMANENT = True # 如果设置为True,则关闭浏览器session就失效。
SESSION_USE_SIGNER = False # 是否对发送到浏览器上session的cookie值进行加密
SESSION_KEY_PREFIX = 'session:' # 保存到session中的值的前缀
# 动态追踪修改设置,如未设置只会提示警告
SQLALCHEMY_TRACK_MODIFICATIONS = True
# 查询时会显示原始SQL语句
SQLALCHEMY_ECHO = True
app.config.from_object(Config)
# 连接redis
redis.init_app(app)
# 连接session
session_store.init_app(app)
# 连接数据库
db.init_app(app)
@app.route('/')
def index():
session['username'] = 'xyw'
return 'ok'
@app.route('/get_session')
def get_session():
print(session['username'])
return 'ok'
@app.route("/redis1")
def set_redis():
redis.set("username","xiaohuihui")
redis.hset("brother","zhangfei","17")
return "ok"
@app.route('/redis2')
def get_redis():
user = redis.get('username').decode()
print(user)
brother = redis.hget('brother', 'zhangfei').decode()
print(brother)
redis.hdel('brother', 'zhangfei')
return 'ok'
if __name__ == '__main__':
# with app.app_context():
# db.create_all()
app.run()
什么是蓝图?
随着flask程序越来越复杂,我们需要对程序进行模块化的处理,之前学习过python的模块化管理,于是针对一个简单的flask程序进行模块化处理
简单来说,Blueprint 是一个存储视图方法的容器,这些操作在这个Blueprint 被注册到一个应用之后就可以被调用,Flask 可以通过Blueprint来组织URL以及处理请求。
Flask使用Blueprint让应用实现模块化,在Flask中,Blueprint具有如下属性:
但是一个Blueprint并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中。
Blueprint对象用起来和一个应用/Flask对象差不多,最大的区别在于一个 蓝图对象没有办法独立运行,必须将它注册到一个应用对象上才能生效
通常有4步:
1.初始化蓝图对象(/应用/__init__.py
)
2.编写视图(/应用/views.py)
3.注册视图(/应用/__init__.py
)
4.注册蓝图(/main.py)
如果要关联数据库:
1.初始化SQLAlchemy(/database.py)
2.写模型类对象(/应用/models.py)
3.连接数据库并导入模型(/main.py)
/应用/__init__.py
:
from flask import Blueprint
# 1.初始化蓝图对象
users_blue = Blueprint(
'users', __name__,
template_folder='users_templates', # 页面存放的目录
static_folder='users_static', # 静态文件存放的目录
static_url_path='/static', # 提供给外部访问的路径
)
# 3. 注册视图
from .views import *
/应用/views.py:
from . import users_blue
from flask import render_template
# 2.编写视图
@users_blue.route('/')
def index():
return render_template('index.html', title='users主页面')
main.py:
from flask import Flask
app = Flask(__name__)
class Config():
DEBUG = True
# 数据库链接配置
# SQLALCHEMY_DATABASE_URI = "mysql://账号:密码@IP/数据库名?编码"
SQLALCHEMY_DATABASE_URI = "mysql://root:[email protected]:3306/students?charset=utf8mb4"
# 动态追踪修改设置,如未设置只会提示警告
SQLALCHEMY_TRACK_MODIFICATIONS = True
# 查询时会显示原始SQL语句
SQLALCHEMY_ECHO = True
app.config.from_object(Config)
# 连接数据库
from database import db
db.init_app(app)
from users.models import Member
# 4. 注册蓝图
from users import users_blue
app.register_blueprint(users_blue,url_prefix='/users')
if __name__ == '__main__':
# with app.app_context():
# db.create_all()
app.run()
database.py:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
/应用/models.py:
from database import db
class Member(db.Model):
__tablename__ = "tb_member"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(250), comment="姓名")
avatar = db.Column(db.String(250), comment="头像")
age = db.Column(db.Integer, comment="年龄")
sex = db.Column(db.Boolean, default=False, comment="性别")
money = db.Column(db.DECIMAL(8,2), nullable=True, comment="钱包")
achievement_list = db.relationship("Achievement", uselist=True, backref="student", lazy="select")
# backref 反向引用,类似django的related,通过外键模型查询主模型数据时的关联属性
info = db.relationship("StudentInfo", backref="own", uselist=False)
def __repr__(self):
return self.name
index.html:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<p>蓝图下的模板p>
<h1>{{ title }}h1>
<p><img src="/users/static/1.jpg" alt="">p>
body>
html>
manager.py
:
from application import init_app
manager = init_app('application.settings.dev')
if __name__ == '__main__':
manager.run()
application/__init__.py
:
load_config:(utils/__init__.py
)
def load_config(config_path):
# import_module 根据字符串路径直接进行模块导包,识别模块中的变量成员
# "application.settings" / "application.settings.dev(prod)"
module = import_module(config_path)
config_file_name = config_path.split('.')[-1]
if config_file_name == 'settings':
return module.InitConfig
return module.Config
settings/__init__.py
:(默认配置)
class InitConfig():
DEBUG = True
# 总路由的地址
URL_PATH = "application.urls"
# 蓝图列表
INSTALLED_APPS = [
]
# 数据库链接配置
# SQLALCHEMY_DATABASE_URI = "mysql://账号:密码@IP/数据库名?编码"
SQLALCHEMY_DATABASE_URI = ""
# 动态追踪修改设置,如未设置只会提示警告
SQLALCHEMY_TRACK_MODIFICATIONS = True
# 查询时会显示原始SQL语句
SQLALCHEMY_ECHO = True
settings/dev.py:(继承默认配置,开发配置)
from . import InitConfig
class Config(InitConfig):
# 注册应用
INSTALLED_APPS = [
'application.apps.home',
]
# 连接数据库配置
SQLALCHEMY_DATABASE_URI = "mysql://root:[email protected]:3306/students?charset=utf8mb4"
通过自定义方法(load_command):
utils/__init__.py
:
def load_command(manager, command_path):
module = import_module(command_path)
# 搜索当前模块下的所有类
class_list = inspect.getmembers(module, inspect.isclass)
for class_name, class_object in class_list:
if issubclass(class_object, Command) and class_name != 'Command':
# 别名 对象
manager.add_command(class_object.name, class_object)
utils/commands.py
:
from flask_script import Command, Option
import os
class BluePrintCommand(Command):
name = "blue" # 命令的调用别名
option_list = [
Option("--name","-n",help="蓝图名称")
]
def run(self,name=None):
if name is None:
print("蓝图名称不能为空!")
return
if not os.path.isdir(name):
os.mkdir(name)
open("%s/views.py" % name,"w")
open("%s/models.py" % name,"w")
with open("%s/urls.py" % name,"w") as f:
f.write("""from . import views
urlpatterns = [
]
""")
def include(url, blueprint_func):
return {'url_prefix': url, 'path': blueprint_func}
def path(url, view_func):
return {'rule': url, 'view_func': view_func}
def load_blueprint(app,db):
# 获取总路由的路由列表( [include('/home', 'home.urls')]=>[{'url_prefix': /home, 'path': home.urls}] )
app_url_list = import_module(app.config.get('URL_PATH')).urlpatterns
# 获取蓝图名称和应用( ['application.apps.home', ....] )
for blueprint_path in app.config.get('INSTALLED_APPS'):
blueprint_name = blueprint_path.split('.')[-1]
for blueprint_url in app_url_list:
if blueprint_url.get('path') == blueprint_name + '.urls':
url_prefix = blueprint_url.get('url_prefix')
break
# 创建蓝图对象
blue = Blueprint(blueprint_name, blueprint_path)
# 蓝图对象注册路由 (urlpatterns=[path('/index', views.index)...]=>{'rule':/index,'view_func':views.index})
blue_urls_module = import_module(blueprint_path+'.urls')
for urls_item in blue_urls_module.urlpatterns:
blue.add_url_rule(**urls_item)
# 注册蓝图
app.register_blueprint(blue, url_prefix=url_prefix)
# 加载当前蓝图下的模型
import_module(blueprint_path+'.models')
总路由(urls.py):
from application.utils import include
urlpatterns = [
include('/home', 'home.urls')
]
apps/home/urls.py:
from . import views
from application.utils import path
urlpatterns = [
path('/index', views.index)
]
https://gitee.com/wangfan741/flag-framework