python后端:Flask框架学习笔记

笔记来自视频教程:2分钟了解flask_哔哩哔哩_bilibili
一、第一个flask程序
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

二、run启动参数详解
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请求

请求和响应:

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!'
七、Response:响应

从服务端(前端)向客户端发送的响应

@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)
九、会话技术
1.cookie

首先我们明确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,返回特定用户数据

2.session

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

十、模板Template

模板就是呈现给用户的界面

在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博客

十一、模型Model

它用于对数据库进行操作

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步命令行操作

1.表的增操作:
#增:添加数据
@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'
2.表的删操作:
#删:减少数据
@blue.route('/userdelete')
def user_delete():
    u=User.query.first()#查询第一条数据
    db.session.delete(u)
    db.session.commit()
    return 'sucess'
3.表的改操作:
@blue.route('userupdate')
def user_update():
    u = User.query.first()  # 查询第一条数据
    u.age=1000#拿到直接改
    db.session.commit()
    return 'sucess'
4.表的查操作:

查操作就比较麻烦了,需要记的东西有很多:

查询数据
        过滤器
                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:查询返回的记录总数
5.多表操作:

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插件 

之前已经有提到过了,像之前的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) # 可以抛出错误给用户
十三、Flask内置对象&配置templates,static

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:用来删除数据

十五、RESTful类视图

他的作用主要用在前后端分离的项目中,视图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博客

  • 14
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值