赞
踩
- from flask import Flask #导入必要的包
- app=Flask(__name__) #创建Flask对象
- @app.route('/') #前端在根目录下的路由+视图函数p
- def p():
- return 'he'
- app.run() #运行该Flask对象 app
这是我们做flask项目时候的具体流程,我们不用担心看不太懂,只要了解大概的模式就好
我们从浏览器进入http://127.0.0.1:5000/
就可以看到打印出来的he了(后端向前端返回的数据)
- from flask import Flask #导入必要的包
- app=Flask(__name__) #创建Flask对象
- @app.route('/') #前端在根目录下的路由+视图函数p
- def p():
- return 'he'
-
- @app.route('/user/') #前端在根目录下的user下的路由+视图函数p
- def user():
- return 'user'
- app.run()
在根目录下访问 http://127.0.0.1:5000/user/会打印出
来user
- from flask import Flask #导入必要的包
- app=Flask(__name__) #创建Flask对象
- @app.route('/') #前端在根目录下的路由+视图函数p
- def p():
- return 'he'
- app.run(debug=True,port=5001,host='0.0.0.0')
- #run()启动可以添加参数
- #debug是开调试模式,修改python代码自动重新运行,我们只要刷新浏览器
- #port指定端号,默认5000
- #host主机,默认是127.0.0.1,指定位0.0.0.0代表本机所有ip都可以访问网站
我们可以看到。网页上的字体有各种颜色和字型,这就要用到渲染技巧:
如我们在函数返回时return '<b>Flask</b>'那么出来的Flask就会加粗,所有之处HTML的渲染方式,但是我们一般不这么做,我们一般把所有的渲染语句放到templates文件之中
- from flask import Flask, render_template # 导入必要的包
- app=Flask(__name__) #创建Flask对象
- @app.route('/') #前端在根目录下的路由+视图函数
- def user():
- return render_template('index.html') #使用templates目录下的index.html模板
- app.run()
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
- <h2>index</h2>
- </body>
- </html>
这样程序更加系统,而static文件中会存放一些静态信息.css,如字体颜色等
合理的项目拆分可以让我们更好的把握项目结构,方便编码,如果要做一个大型的项目,项目拆分是必不可少的
首先,我们像这样在项目目录下建立这样几个文件,然后把templates和static放入App文件中
在app.py文件中,我们之前讲过会写这个
对于一个大项目,我们会写很多route和视图函数,这样全部放在app中很繁杂,那么怎么做呢?
我们像这样拆分文件,让App变成一个包,init用于把项目初始化,models是模型和数据库相关操作,views中存放所有的route和视图函数
但是view文件中,我们需要app创建,app.py文件中,我们也需要app的创建,如果导两次包那么就创建了两个app对象,这不就不对了吗?所以我们要引入一个新概念,叫做蓝图blueprint
1.蓝图用来规划urls(路由route)
2.我们在views.py中初始化蓝图
blue = Blueprint('user',__name__)
然后再init文件中调用蓝图进行路由注册
app.register_blueprint(blueprint=blue)
init.py文件:
- #初始化文件,创建flask应用
- from flask import Flask
- from .views import * #init里导入了view,那么view就不要再导入init了避免重复导入
- def create_app():#用函数进行封装,导包时候只会声明这个函数
- app=Flask(__name__)#新建了一个Flask对象
- app.register_blueprint(blueprint=blue)#让app和我们再view文件中的blue联系起来
- return app
views.py文件:
- from flask import Blueprint
- from .models import * #目前不用,但是一定会用的
- blue=Blueprint('user',__name__)#可以有多个blue,作为模板提供给app注册
- blue2=Blueprint('product',__name__)
- @blue.route('/')
- def index():
- return 'hello'
- @blue2.route('/goods')
- def goods():
- return 'goods'
app.py文件:
- from App import create_app
- app=create_app()
- if __name__=='__main__':
- app.run(debug=True)
是不是一下子就清晰很多了?
- from flask import Flask
- app=Flask(__name__)
- @app.route('/string/<string:name>') #尖括号里面写类型:变量名
- def get_string(name):
- return name
- # app.run()
- # string 接受任何不包含斜杠的文本
- # int 接受正整数
- # float 接受正浮点数
- # path 接受包含斜杠的文本
- # uuid 只接受uuid字符串,唯一码,一种生成规则
- # any 可以同时指定多个路径,进行限定
- @app.route('/any/<any(apple,banana,orange):fruit>')#只能传入三个里面的一个
- def get_any(fruit):
- return str(fruit)
-
-
- #请求方法,默认支持GET,HEAD,OPTIONS,如果要支持,需要自己指定
- @app.route('/methods',methods=['GET,POST,HEAD,PUT,DELETE'])
- def l():
- return 1
- app.run()
-
请求和响应:
request:请求
response:响应
http一次前后端交互:先请求,后响应
服务器在接受到客户端的请求后,会自动创建request对象,他的重要参数如下:
url:完整请求地址
base_url:去掉GET参数的URL
host_url:只有主机和端口号的URL
path:路由中的路径
method:请求方法 GET/POST
remote_addr:请求的客户端地址
args:GET请求参数
form:POST请求参数
files:文件上传
headers:请求头
cookies:请求中的cookie(用于特别标识是具体哪个用户发送的请求)
- @blue.route('/request',methods=['GET','POST'])
- def get_request():
- pass
- print(request) #打印出了:<Request 'http://127.0.0.1:5000/request' [GET]> 就是网址+请求方式
- print(request.method)#只打印请求方式
- #GET请求
- print(request.args)#打印请求附带的参数 如'http://127.0.0.1:5000/request/?name=lisi&age=33' ?后面是参数那么就会打印 ImmutableMultiDict([('name','lisi'),('age','33')])这是一个类字典对象,它可以出现重复的key
- print(request.args['name'])#单独把name参数打印出来
- print(request.args.get('name'))#单独把name参数打印出来的另一种写法
- print(request.args.getlist('name'))#单独把name参数打印出来,如果有多个name参数就全部打印
- #POST请求
- print(request.form)#返回post的参数字典 如'http://127.0.0.11:5000/request',data={'name':'lucy','age':33}
- print(request.form.get('name'))#单独把name参数打印出来
- #cookie
- print(request.cookies)#把附带的cookie参数打印出来
- print(request.path)#打印到/reuest/
- print(request.url)#打印所有,包括附带参数
- print(request.base_url)#去参数
- print(request.host_url)#只到根目录
- print(request.remote_addr)#127.0.0.1,客户端的ip
- print(request.file)#打印文件内容,还是那个类字典,需要有文件上传
- print(request.header)#请求头
- return 'request ok!'
从服务端(前端)向客户端发送的响应
- @blue.route('/respose/')
- def get_response():
- pass
- #响应的几种方式
- #1.返回字符串(并不常用)
- #return 'response ok!'
- #2.模板渲染
- #return render_template('index.html')使用template文件中的index.html渲染方式
- #3.返回json数据(前后端分离)
- data={'name':'李四','age':'44'}
- #return data 一般返回json数据 可以用字典实现
- #或者序列化 jsonify(): 字典->字符串
- #return jsonify(data)
- #自定义Response对象
- html=render_template('index.html')
- res=make_response(html,200)#使用自定义的html文件
- #return res
八、Redirect:重定向
就是你访问我,我不返回,我把你的请求跳转到别的页面去,使得项目呈现多层的结构
- @blue.route('/redirect')
- def make_redirect():
- pass
- #重定向的几种方式
- #return redirect('https://www.qq.com') 这样你在返回这个页面的时候会直接跳转到qq页面去
- #return redirect('/response/')#如果本地页面,那么不要网址,直接写路由就可以
- ret=url_for('user.goods')#这里写蓝图名.视图函数名可以转到对应页面 这就是反向解析,我先通过视图函数找到蓝图,再跳转到对应页面去
- #return redirect(ret)
首先我们明确http协议的特点:
1.先请求,后响应
2.响应后会断开链接
3.一次请求就结束了
用图表示:
如图所示:每个用户是一个浏览器(前端)访问服务器后会生成一个cookie,可以是用户名等等可以唯一标识的字符串,这样在下一次访问服务器时,就会让服务器找到你是哪个用户,返回对应的数据
具体处理流程如下:
- from flask import Blueprint, request, make_response, render_template, redirect, url_for
- import requests
- blue=Blueprint('user',__name__)#可以有多个blue,作为模板提供给app注册
- blue2=Blueprint('product',__name__)
- @blue.route('/')
- def index():
- return 'hello'
- @blue.route('/home')
- def home():
- # 4.获取cookie
- username=request.cookies.get('user')#这就是字典的意义,通过键找到值
- return render_template('home.html',username=username)
- @blue.route('/login',methods=['GET','POST'])
- def login():
- # GET:访问登录页面
- if request.method=='GET':
- return render_template('login.html')
- # POST:实现登录功能(按下提交按钮就是一个POST请求)
- elif request.method=='POST':
- pass
- #1.获取前端提交过来的数据
- username=request.form.get('username')
- password=request.form.get('password')
- #2.模拟登录:用户名密码验证
- if username=='lisi'and password=='123':
- response=redirect('home')#成功登录,重定向转到home页面
- #3.设置cookie,实现如果登录成功识别出是哪个用户
- response.set_cookie('user',username)#这个函数和字典一样,需要前key后值,这里把username作为特定识别的cookie
- #默认浏览器关闭则cookie失效
- #过期时间:max_age 单位秒
- # response.set_cookie('user',username,max_age=3600*24*7)持续7天
- # response.set_cookie('user',username,expires=datetime。datetime(2024,11.11))规定时间
- return response
- else:
- return '用户名或密码错误!'
- #注销
- @blue.route('/logout')
- def logout():
- response=redirect('/home')
- response.delete_cookie('user')#把需要删除的cookie写进去
- return response
以上是一个登录页面的实现,其中:
home.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>首页</title>
- </head>
- <body>
- <h2>首页</h2>
- <hr>
-
- <a href="/login">登录</a>
- </body>
- </html>
login.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>登录</title>
- </head>
- <body>
- <h2>登录</h2>
- <hr>
- <form action=""method="post">
- <!-- 前半段是空的表示默认为login当前界面-->
- <!-- <form action="{{url_for('user.login')}}"></form>反向解析也可以-->
- <!-- <form action="/login"></form>直接写也可以-->
- <p>
- 用户名:<input type="text"name="username">
- </p>
- <p>
- 密码:<input type="password"name="password">
- </p>
- <p>
- <button>提交</button>
- </p>
- </form>
- </body>
- </html>
实现了用户输入用户名密码,后端判断是否正确并给予cookie,返回特定用户数据
session是服务器端会话技术,依赖于cookie
特点:
1.服务器端的会话技术
2.所有数据存储在服务器中
3.默认存储在内存中
4.存储结构也是key-value形式,键值对
5.session是离不开cookie的
flask中的session是全局对象(之前的request也是全局对象)
常用操作:
设置session
session['key']='value'
获取session
session.get(key,default=None)根据键获取会话的值
删除session
session.pop(key)删除一个
session.clear()删除所有
书接上回,我们设置session:
- from flask import Blueprint, request, make_response, render_template, redirect, url_for, session
- import requests
- blue=Blueprint('user',__name__)#可以有多个blue,作为模板提供给app注册
- blue2=Blueprint('product',__name__)
- @blue.route('/')
- def index():
- return 'hello'
- @blue.route('/home')
- def home():
- # 4.获取cookie
- username=request.cookies.get('user')#这就是字典的意义,通过键找到值
- #获取session
- username=session.get('user')
- return render_template('home.html',username=username)
- @blue.route('/login',methods=['GET','POST'])
- def login():
- # GET:访问登录页面
- if request.method=='GET':
- return render_template('login.html')
- # POST:实现登录功能(按下提交按钮就是一个POST请求)
- elif request.method=='POST':
- pass
- #1.获取前端提交过来的数据
- username=request.form.get('username')
- password=request.form.get('password')
- #2.模拟登录:用户名密码验证
- if username=='lisi'and password=='123':
- response=redirect('home')#成功登录,重定向转到home页面
- #3.设置cookie,实现如果登录成功识别出是哪个用户
- response.set_cookie('user',username)#这个函数和字典一样,需要前key后值,这里把username作为特定识别的cookie
- #默认浏览器关闭则cookie失效
- #过期时间:max_age 单位秒
- # response.set_cookie('user',username,max_age=3600*24*7)持续7天
- # response.set_cookie('user',username,expires=datetime。datetime(2024,11.11))规定时间
-
-
- #设置session
- session['user']=username
- return response
- else:
- return '用户名或密码错误!'
- #注销
- @blue.route('/logout')
- def logout():
- response=redirect('/home')
- response.delete_cookie('user')#把需要删除的cookie写进去
- session.pop('user')
- return response
在设置session时,一定要先在创建app时(在init.py文件)设置一个secret key(唯一)否则会报错,它的作用是通过这个secret key加密我们session的value值
- #初始化文件,创建flask应用
- from flask import Flask
- from .views import *
- def create_app():#用函数进行封装,导包时候只会声明这个函数
- app=Flask(__name__)#新建了一个Flask对象
- print(app.config)
- """
- <Config {'DEBUG': False, 'SECRET_KEY': None, }这里我们要设置一个secret key
- """
- app.config['SECRET_KEY']='abc123'#随便写,只要是唯一的
- app.register_blueprint(blueprint=blue)#让app和我们再view文件中的blue联系起来
- return app
然后我们打开网站在输入用户名密码后点击检查看看我们的session:
发现我们的value已经被加密,并且没有报错
但是session也是有过期时间的,怎么设置它是永久的呢?
只需要在设置session那一步下面加上
- #设置session
- session['user']=username
- session.permanent=True
session和cookie的区别:
cookie:
1.在浏览器存储
2.安全性较低
3.可以减轻服务器压力
session:
1.在服务器端存储
2.安全性较高
3.对服务器要求较高
4.依赖cookie
模板就是呈现给用户的界面
在MVT中作为T的角色,V和T是多对多的关系
模板处理分为两个部分
1.静态HTML
2.动态插入代码块
flask主用的HTML模板语言为Jinja2
模板语法
模板语法主要分为两种
变量
标签
模板中的变量 {{ var }}
视图传递给模板的数据
前面定义出来的数据
变量不存在,默认忽略
模板中的标签 {% tag %}
控制逻辑
使用外部表达式
创建变量
宏定义
结构标签
block 块操作
父模板挖坑,子模板填坑
{% block xxx %}
{% endblock %}
extends 继承
{% extends 'xxx' %}继承后保留块中的内容
{{ super() }}
include
包含,将其他html包含进来
{% include 'xxx' %}marco 【了解】
宏定义,可以在模板中定义函数,在其它地方调用
{% macro hello(name) %}
{{ name }}
{% endmacro %}宏定义可导入
{% from 'xxx' import xxx %}
循环
for循环
{% for item in cols %}
AA
{% else %}
BB
{% endfor %}
可以使用和Python一样的for...else
也可以获取循环信息 loop
loop.first: 判断是否是第一个元素
loop.last: 判断是否是最后一个元素
loop.index: 1开始的下标
loop.index0: 0开始的下标
loop.revindex: 反向下标,不包括0
loop.revindex0: 反向下标,包括0
过滤器(扩展)
语法
{{ 变量|过滤器|过滤器... }}
capitalize
lower
upper
title
trim
reverse
striptags 渲染之前,将值中标签去掉
safe
default(1)
last
first
length
sum
sort
具体参照链接:Jinja2模板语言最基础入门_jinja2模板语法-CSDN博客
它用于对数据库进行操作
Flask模型
Flask默认并没有提供任何数据库操作的API
我们可以选择任何适合自己项目的数据库来使用
Flask中可以自己的选择用原生语句实现功能,也可以选择ORM(SQLAlchemy,MongoEngine)原生SQL缺点
代码利用率低,条件复杂代码语句越⻓,有很多相似语句
一些SQL是在业务逻辑中拼出来的,修改需要了解业务逻辑直接写SQL容易忽视SQL问题
ORM
Flask通过Model操作数据库,不管你数据库的类型是MySql或者Sqlite特,Flask自动帮你生成相应数据库类型的SQL语句,所以不需要关注SQL语句和类型,对数据的操作Flask帮我们自动完成。只要会写Model就可以了。
Flask使用对象关系映射(Object Relational Mapping,简称ORM)框架去操控数据库。
ORM(Object Relational Mapping)对象关系映射,是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。
将对对象的操作转换为原生SQL
优点
易用性,可以有效减少重复SQL
性能损耗少
设计灵活,可以轻松实现复杂查询
移植性好
下面是代码讲解:
我们之前在views里是没有写import model的,但是一旦我们用到了model,就要在views中加上
from .models import *
1.首先我们要先配置数据库:在init.py文件中配置·sqlite数据库
- def create_app():#用函数进行封装,导包时候只会声明这个函数
- app=Flask(__name__)#新建了一个Flask对象
- #注册蓝图
- app.register_blueprint(blueprint=blue)#让app和我们再view文件中的blue联系起来
- #配置数据库(连接数据库)
- db_url='sqlite://sqlite3.db'
- app.config['SQLALCHEMY_DATABASE_URI']=db_url
- return app
'运行
现在还不会自动生成数据库
我们还要在App下新建一个包,作为我们之后要用很多插件要用的包取名为exts.py
- #exts:插件管理
- #扩展的第三方插件
-
- #1.导入第三方插件
- from flask_sqlalchemy import SQLAlchemy
- from flask_migrate import Migrate
-
- #2.初始化
- db=SQLAlchemy()#ORM
- migrate=Migrate()#创建数据迁移对象
-
- #3.和app对象绑定
第三步犯了难,app对象在init.py里,怎么绑定?为了不循环引用
app.py->_init_.py->view->model 之后还会再model里导入->exts,所以exts里最好不要导入init从而引入app了,而且每多一个插件,init.py就要修改,因此
- #3.和app对象绑定
- def init_exts(app):
- db.init_app(app)
- migrate.init_app(app=app,db=db)#两个参数必须填,用于对应数据库和app
'运行
使用函数的形式,然后在init中
- #初始化文件,创建flask应用
- from flask import Flask
- from .views import *
- from .exts import init_exts
- def create_app():#用函数进行封装,导包时候只会声明这个函数
- app=Flask(__name__)#新建了一个Flask对象
- #注册蓝图
- app.register_blueprint(blueprint=blue)#让app和我们再view文件中的blue联系起来
- #配置数据库(连接数据库)
- db_url='sqlite://sqlite3.db'
- app.config['SQLALCHEMY_DATABASE_URI']=db_url
-
- #和插件绑定
- init_exts(app=app)
-
- return app
只用改exts就可以啦
如此配置工作完成,下面写model
- #model:模型,数据库
- from .exts import db #导入db数据库对象
-
-
- #模型 数据库
- #类 表
- #类属性 表字段
- #一个对象 表的一行
-
-
- #模型model:一个类
- #必须继承db.Model,它才是一个模型,不然就只是一个class
-
- class User(db.Model):
- #表名
- __tablename__='tb_user'
- #定义表字段
- id=db.Column(db.Integer,primary_key=True,autoincrement=True)
- name=db.Column(db.String(30),unique=True,index=True)
- age=db.Column(db.Boolean,default=True)
- #autoincrement自动递增
- #index普通索引
-
之后进行数据迁移
数据迁移命令:
在cmd或Terminal先进入项目目录(app.py所在目录):
然后输入命令:
flask db init 创建迁移文件夹migrates, 只调用一次
flask db migrate 生成迁移文件 会自动找到model中自动生成的类(继承了db)
flask db upgrade 执行迁移文件中的升级 把数据库中的表创建了
flask db downgrade 执行迁移文件中的降级 把之前的upgrade撤销 回到上一个版本
如果想改变表结构(改动了model.py)就要重新做迁移 也就是做2 3步命令行操作
- #增:添加数据
- @blue.route('/useradd')
- def user_add():
- # #添加一条数据:
- # u=User()
- # u.name='kun'
- # u.age=1
- # u.id=0
- # db.session.add(u)#将u对象添加到session中
- # db.session.commit()#同步到数据库中
- #同时添加多条数据:
- users=[]
- for i in range(10,30):
- u=User()
- u.name='男的'+str(i)
- u.age=i
- users.append(u)
- try:
- db.session.add_all(users)
- db.session.commit()#自带事务提交
- except Exception as e:
- db.session.rollback()#回滚撤销操作
- db.session.flush()#清空缓存
- return 'fail'+str(e)
- return 'success'
- #删:减少数据
- @blue.route('/userdelete')
- def user_delete():
- u=User.query.first()#查询第一条数据
- db.session.delete(u)
- db.session.commit()
- return 'sucess'
- @blue.route('userupdate')
- def user_update():
- u = User.query.first() # 查询第一条数据
- u.age=1000#拿到直接改
- db.session.commit()
- return 'sucess'
查操作就比较麻烦了,需要记的东西有很多:
查询数据
过滤器
filter() 把过滤器添加到原查询上,返回一个新查询得到一个查询集=where,可以list强转,可以.filter.filter无限循环
filter_by() 把等值过滤器添加到原查询上,返回一个新查询
limit() 使用指定的值限制原查询返回的结果数量,返回一个新查询
offset() 偏移原查询返回的结果,返回一个新查询
order_by() 根据指定条件对原查询结果进行排序,返回一个新查询
group_by() 根据指定条件对原查询结果进行分组,返回一个新查询常用查询
all() 以列表形式返回查询的所有结果,返回列表
first() 返回查询的第一个结果,如果没有结果,则返回 None
first_or_404() 返回查询的第一个结果,如果没有结果,则终止请求,返回 404 错误响应
get() 返回指定主键对应的行,如果没有对应的行,则返回 None
get_or_404() 返回指定主键对应的行,如果没找到指定的主键,则终止请求,返回 404 错 误响应
count() 返回查询结果的数量
paginate() 返回一个 Paginate 对象,它包含指定范围内的结果查询属性:
contains
startswith
endswith
in_
__gt__
__ge__
__lt__
__le__逻辑运算
与 and_
filter(and_(条件),条件...)
或 or_
filter(or_(条件),条件...)
非 not_
filter(not_(条件),条件...)
查询:
- persons = Person.query.all() # 获取所有
- persons = Person.query.filter(Person.age>22)
- # filter功能比filter_by强大
- persons = Person.query.filter(Person.age==22) # filter(类.属性==值)
- persons = Person.query.filter_by(age=22) # filter_by(属性=值)
- persons = Person.query.filter(Person.age.__lt__(22)) # <
- persons = Person.query.filter(Person.age.__le__(22)) # <=
- persons = Person.query.filter(Person.age.__gt__(22)) # >
- persons = Person.query.filter(Person.age.__ge__(22)) # >=
-
- persons = Person.query.filter(Person.age.startswith('宝')) # 开头匹配
- persons = Person.query.filter(Person.age.endswith('宝')) # 结尾匹配
- persons = Person.query.filter(Person.age.contains('宝')) # 包含
- persons = Person.query.filter(Person.age.in_([11,12,22])) # in_
-
- persons = Person.query.filter(Person.age>=20, Person.age<30) # and_
- persons = Person.query.filter(and_(Person.age>=20, Person.age<30)) # and_
- persons = Person.query.filter(or_(Person.age>=30, Person.age<20)) # or_
- persons = Person.query.filter(not_(Person.age<30)) # not_
-
排序:
- persons = Person.query.order_by('age') # 升序
- persons = Person.query.order_by(desc('age')) # 降序
分页:
- perrsons = Person.query.limit(5) # 取前5个
- persons = Person.query.offset(5) #跳过前5个
-
- # 获取⻚码page和每⻚数量num
- page = int(request.args.get('page'))
- per_page = int(request.args.get('per_page'))
-
- # 手动做分⻚
- persons = Person.query.offset((page-1) * per_page).limit(per_page)
- # 使用paginate做分⻚
- persons = Person.query.paginate(page=page, per_page=per_page,
- error_out=False).items
-
- paginate对象的属性:
- items:返回当前⻚的内容列表
- has_next:是否还有下一⻚
- has_prev:是否还有上一⻚
- next(error_out=False):返回下一⻚的Pagination对象
- prev(error_out=False):返回上一⻚的Pagination对象
- page:当前⻚的⻚码(从1开始)
- pages:总⻚数
- per_page:每⻚显示的数量
- prev_num:上一⻚⻚码数
- next_num:下一⻚⻚码数
- total:查询返回的记录总数
1.一对多
- #model.py文件:
-
- from .exts import db #导入db数据库对象
-
- #多表关系
- #一对多
- #班级:学生=1:N
- #班级表
- class Grade(db.Model):
- __tablename__='grade'
- id=db.Column(db.Integer,primary_key=True,autoincrement=True)
- name=db.Column(db.String(30),unique=True)
- #建立关联
- #第一个参:是关联的表名
- #第二个参:是反向引用的名称,grade对象 让student去反过来得到grade对象的名称:student.grade
- #为什么这么做?因为我们只给grade的id创建外键,student只能拿到Grade.id,不能拿到name的值,但是反向查找可以找到所有值
- #第三个参:懒加载 事先不给你加关联,等到你真正要用这个关联时再关联
- students=db.relationship('student',backref='grade',lazy=True)
- #这里的students不是字段,就是一个类属性
- #学生表
- class student(db.Model):
- __tablename__='student'
- id=db.Column(db.Integer,primary_key=True,autoincrement=True)
- name=db.Column(db.String(30),unique=True)
- age=db.Column(db.Integer)
- #外键 和Grade表中的id关联
- gradeid=db.Column(db.Integer,db.ForeignKey(Grade.id))
-
- #view.py文件
- import random
-
- from flask import Blueprint
- from .models import *
- blue=Blueprint('user',__name__)
- blue2=Blueprint('product',__name__)
- @blue.route('/')
- def index():
- return 'hello'
-
- @blue.route('/addgrade')
- def addgrade():
- #先添加班级
- grades=[]
- for i in range(10):
- grade=Grade()
- grade.name=f'class{i}'
- grades.append(grade)
- try:
- db.session.add_all(grades)
- db.session.commit()
- except Exception as e:
- print(e)
- db.session.rollback()
- db.session.flush()
- return 'OK'
-
- @blue.route('/addstudent')
- def addstu():
- #添加学生
- students=[]
- for i in range(10,20):
- stu=student()
- stu.name=f'student{i}'
- stu.age=i
- stu.gradeid=random.randint(0,10)
- students.append(stu)
- try:
- db.session.add_all(students)
- db.session.commit()
- except Exception as e:
- print(e)
- db.session.rollback()
- db.session.flush()
- return 'OK'
-
- @blue.route('/updatestu')
- def upgradestu():
- stu=student.query.first()
- stu.age=100
- db.session.commit()
- return 'OK'
- @blue.route('/deletestu')
- def deletestu():
- stu=student.query.first()
- db.session.delete(stu)
- db.session.commit()
- return 'ok'
- @blue.route('/searchstu')
- def serchstu():
- stu=student.query.get(2)
- print(stu.name)
- print(stu.age)
- print(stu.grade.name)
- return 'OK'
2.多对多
如图所示:用户表和电影表就是多对多的关系
- #用户收藏电影,一个用户可以收藏多部电影, 一部电影可以被不同的用户收藏, 是一个多对多关系.
- # 中间表(不是类)
- collects = db.Table('collects',
- # user_id为表字段名称, user.id为外键表的id
- db.Column('user_id', db.Integer, db.ForeignKey('user.id'), primary_key=True),
- db.Column('movie_id', db.Integer, db.ForeignKey('movie.id'), primary_key=True)
- )
-
-
- class Movie(db.Model):
- id = db.Column(db.Integer, primary_key=True, autoincrement=True)
- name = db.Column(db.String(200))
-
- class User(db.Model):
- id = db.Column(db.Integer, primary_key=True, autoincrement=True)
- name = db.Column(db.String(16))
- age = db.Column(db.Integer, default=1)
- # 多对多 关联的学生表格的模型, 中间表的名称, 反向查找
- movies = db.relationship('Movie', backref='users',
- secondary=collects, lazy='dynamic')
lazy属性:
懒加载,可以延迟在使用关联属性的时候才建立关联
lazy='dynamic': 会返回一个query对象(查询集),可以继续使用其他查询方法,如all().
lazy='select': 首次访问到属性的时候,就会全部加载该属性的数据. 我已经查完了,不能进行其他操作了
lazy='joined': 在对关联的两个表进行join操作,从而获取到所有相关的对象
- #查:
- # 查询用户收藏的所有电影
- user = User.query.get(id)
- movies = user.movies
- # 查询电影被哪些用户收藏
- movie = Movie.query.get(id)
- users = movie.users
- #删:
- # 中间表的数据会被级联删除
- movie = Movie.query.get(id)
- db.session.delete(movie)
- db.session.commit()
- #增:
- # 用户收藏电影
- user = User.query.get(id)
- movie = Movie.query.get(id)
- user.movies.append(movie)
- db.session.commit()
之前已经有提到过了,像之前的flask-migrate等都是flask插件内容,特征是都带有flask-标志
下面学习几种其他插件
插件的使用非常简单,先安装,然后在exts.py文件中初始化
安装
pip install flask-caching
初始化
from flask_cache import Cache
cache = Cache(config={
'CACHE_TYPE': 'simple',
})
cache.init_app(app=app)
使用
在视图函数上添加缓存
@blue.route('/')
@cache.cached(timeout=30)
def home():##什么意思呢? 其实就是,如果我要访问一个数据,每天访问很多次,都是这个数据访问时##间又特别长,那么我们就加上这个cache,使得第一次加载的时候满,之后每一次加载直##接读缓存,节省时间,timeout=30是缓存的有效时间
print('加载数据')
return 'home'
什么是钩子(中间件Middleware)
钩子或叫钩子函数, 是指在执行函数和目标函数之间挂载的函数, 框架开发者给调用方提供一个point-挂载点, 是一种AOP切面编程思想他相当于一个拦截,在访问与视图函数之间加了一个函数进行检查,如看看是不是有个爬虫或者一直访问服务器或植入木马病毒等等
常用的钩子函数
before_first_request:处理第一次请求之前执行(你第一次来访问我,我执行函数,第二次就不了)
before_request:(用的最多)在每次请求之前执行. 通常使用这个钩子函数预处理一些变量, 实现反爬等.
每一次请求都会执行这个函数
after_request:注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行.teardown_appcontext: 当APP上下文被移除之后执行的函数,可以进行数据库的提交或者回滚
AOP反爬策略
- # 利用缓存反爬,相同ip地址1秒内不允许重复访问
- key = request.remote_addr + "before"
- value = cache.get(key)
- if value:
- return '小伙子,别爬了'
- else:
- cache.set(key, 'aa', timeout=1)
-
- # 反爬,防止非浏览器访问
- ua = request.user_agent # 用户代理
- if not ua:
- return "hello"
- # abort(400) # 可以抛出错误给用户
g:
global全局对象
g对象是专⻔用来保存用户的数据的
g对象在一次请求中的所有的代码的地方,都是可以使用的突破变量存储位置限制, 为数据传递添加了新的方式, 比如我们在before_request产生一个数据在后面需要使用,可以保存在g对象中,在其他视图函数中就可以使用这个数据他和在函数外面定义的对象不一样,他时间只有一次请求的时间,每一个请求都带有一个g对象
request:
请求对象, 可以获取客户端提交过来的所有请求信息
session:
会话技术,服务端会话技术的接口
current_app:
app的配置信息, app对象获取, current_app使用获取当前app需要注意,一定要在程序初始化完成之后
如果想要修改templates模板目录或static静态目录,可以自己配置
在settings.py文件中添加BASE_DIR:
- import os
- BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
'运行
在__init__.py文件中添加static路径和templates路径:
- static_path = os.path.join(settings.BASE_DIR, 'static')
- template_path = os.path.join(settings.BASE_DIR, 'templates')
- app = Flask(__name__, static_folder=static_path, template_folder=template_path)
在views.py文件中访问模板:
- @blue.route('/hello/')
- def hello():
- return render_template('hello.html')
在模板中使用静态资源:
<link rel="stylesheet" href="{{ url_for('static', filename='css/hello.css') }}">
前后端不分离:
render_template('index.html',users=users)
只要使用了这种方法就是前后端不分离,通过本地的模板渲染
前后端分离:
后端返回json字符串:jsonify() 序列化方法
前端使用ajax来请求数据:ajax
HTTP请求方式:
GET:用来获得数据
POST:用来提交数据
PUT:用来修改数据
DELETE:用来删除数据
他的作用主要用在前后端分离的项目中,视图views.py可以完全删除了,我们将用类视图和urls.py来代替他的功能:
用到的插件:flask-restful-0.3.10
项目拆分如下:
在init文件中引入api:
- from flask_sqlalchemy import SQLAlchemy
- from flask_migrate import Migrate
- from flask_restful import Api
- from .urls import * #这里不导入的话url文件注册代码不会执行
- db=SQLAlchemy()
- migrate=Migrate()
- api=Api()
- def init_exts(app):
- db.init_app(app=app)
- migrate.init_app(app=app,db=db)
- api.init_app(app=app)
在apis.py文件中引入类视图:
- from flask_restful import Resource
-
- #类视图:CBV 基于视图的类
- #视图函数:FBV 基于视图的函数
- class HelloResouce(Resource):
- #如果是GET请求自动执行
- def get(self):
- return 'GET请求'
- #如果是POST请求自动执行
- def post(self):
- return 'POST请求'
在urls.py文件中注册类视图:
- #路由文件:专门写路由
- from .exts import api
- from .apis import *
-
- #路由
- api.add_resource(HelloResouce,'/hello')
现在的执行流程就是,类=原来的视图函数
先在apis.py定义类视图,类视图在urls.py中被注册并赋予路由,服务器根据访问类型执行类视图中不同的函数
字段格式化 Flask-RESTful
定义返回给前端的数据格式
具体详细请转向文章:Flask-RESTful的介绍和简单使用-CSDN博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。