Flask——接口路由技术

news2024/11/30 12:43:22

接口路由技术

  • 一、Flask 简介
    • 1、环境安装:
    • 2、一个最小的应用
    • 3、两种运行方式
  • 二、定义路由
    • 1、普通路由
    • 2、动态路由
    • 3、限定类型
    • 4、地址尾部的“/”
  • 三、请求与响应-请求方法
  • 四、请求与响应-处理请求数据
    • 1、request的常用属性/方法
    • 2、get 请求参数
    • 3、json 请求
    • 4、表单请求
    • 5、文件请求
  • 五、请求与响应-处理响应信息
    • 1、文本型
    • 2、元组
    • 3、Json
    • 4、html
    • 5、额外数据
  • 六、测试平台环境配置
  • 七、接口配置
    • 1、flask-restx 介绍
    • 2、插件安装
    • 3、demo 示例
  • 八、接口使用
    • 1、设计框架的原则
      • a、高耦合实例
      • b、低内聚示例
    • 2、编写 RESTFUL 风格的接口
    • 3、添加路由的方式
      • 方式一:
      • 方式二
  • 九、集成swagger
    • 1、namespace 的使用
    • 2、swagger 接口文档配置
      • a、api.parser()用法
      • b、get 请求示例
      • c、post 请求示例
        • 1、处理 json 格式
        • 2、处理 files格式
        • 3、处理 form格式
        • 4、处理choice格式
  • 十、orm介绍
    • 1、什么是持久化
    • 2、什么是 ORM
    • 3、ORM 与 SQL 的对比
    • 4、ORM 优缺点
  • 十一、orm中间件配置
  • 十二、数据库与表管理
    • 创建表操作
    • 删除表操作
  • 十三、对象与数据模型
    • 1、数据模型
    • 2、设计用户表字段
    • 3、Flask-SQLAlchemy 的属性字段定义
    • 4、Flask-SQLAlchemy 字段常用关键字参数
    • 5、Flask-SQLAlchemy 对象与数据模型示例
  • 十四、数据CRUD
    • 1、添加数据(create)
      • a、单条数据新增
      • b、多条数据新增
    • 2、读取数据(read)
      • a、查询表中全部数据
      • b、条件查询——单条件查询
      • c、条件查询——多条件查询
    • 3、修改数据(update)
    • 4、删除数据(delete)
  • 十五、多表关系-一对多
    • 1、一对多关系
    • 2、一对多表结构
    • 3、一对多增删查改
      • a、数据新增
      • b、数据查询
        • 多查一
        • 一查多
      • c、数据修改
        • 一改多
        • 多改一
      • d、数据删除

一、Flask 简介

Flask 是一个轻量级的 web 开发框架。 它依赖 jinja2 和 Werkzeug WSGI 服务的一个微型框架。

1、环境安装:

pip install flask

2、一个最小的应用

# 1. 导入 Flask 模块
from flask import Flask
# 2. 创建Flask应用程序实例
app = Flask(__name__)

# 3. 定义路由及视图函数
@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"
# 4. 启动程序
if __name__ == '__main__':
    app.run()

3、两种运行方式

linux/mac: 命令行运行

$ export FLASK_APP=hello
$ flask run

windows: 命令运行

set FLASK_APP=app.py
flask run

代码调用

if name == ‘main’:
app.run()

二、定义路由

1、普通路由

通过装饰器 @app.route 添加路由

from flask import Flask

#2、创建flask应用程序的实例;__name__==__main__
app=Flask(__name__)

#添加路由
@app.route("/")
def hello_world():
    return "<p>hello world</p>"

@app.route("/demo")
def demo():
    return "<p>hello world 666</p>"

#启动入口
if __name__=='__main__':
    #flask服务启动起来
    #轮询等待的方式,等待浏览器发来请求
    #会一直接收请求,直到程序停止
    app.run()

在这里插入图片描述

2、动态路由

通过 app.route('/user/<username>') 添加动态路由

from flask import Flask

#2、创建flask应用程序的实例;__name__==__main__
app=Flask(__name__)

#添加路由
@app.route("/")
def hello_world():
    return "<p>hello world</p>"

@app.route("/demo")
def demo():
    return "<p>hello world 666</p>"

@app.route("/userinfo/<username>")
def demo1(username):
    return f"hello world 这是{username}同学"

#启动入口
if __name__=='__main__':
    #flask服务启动起来
    #轮询等待的方式,等待浏览器发来请求
    #会一直接收请求,直到程序停止
    app.run()

3、限定类型

路径中添加 <类型:变量名> 来限定变量的类型
@app.route('/post/<int:post_id>')

string:接收任何不包含斜杠的文本
int:接收正整数
float:接收正浮点数
path:类似string,但可以包含斜杠
uuid:接收UUID字符串

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/5/26 07:03
# @Author  : 杜兰特
# @File    : flask_demo.py

#1、导入flask模块
from log_utils import logger

from flask import Flask

#2、创建flask应用程序的实例;__name__ ==__main__
app=Flask(__name__)

#限定类型
@app.route("/user/<int:id>")
def demo2(id):
    return f"hello world 这是{id}"

@app.route("/user/<string:username>")
def demo3(username):
    return f"hello world 这是{username}同学"

#启动入口
if __name__=='__main__':
    #flask服务启动起来
    #轮询等待的方式,等待浏览器发来请求
    #会一直接收请求,直到程序停止
    app.run()

4、地址尾部的“/”

路由的尾部带有“/”(浏览器的地址栏中输入和不输入“/”的效果一样)

路由的尾部没有“/”(输入的 URL 的结尾不能加“/”,会报错)

#1、导入flask模块
from flask import Flask

#2、创建flask应用程序的实例;__name__ ==__main__
app=Flask(__name__)

#添加路由
@app.route("/demo")
def demo():
    return "<p>hello world 666</p>"

#启动入口
if __name__=='__main__':
    #flask服务启动起来
    #轮询等待的方式,等待浏览器发来请求
    #会一直接收请求,直到程序停止
    app.run()

在这里插入图片描述
在这里插入图片描述

三、请求与响应-请求方法

请求 说明

GET 获取服务器资源
POST 新增服务器资源
PUT 更新服务器资源(客户端提供改变后的完整资源)
DELETE 删除服务器资源

接口设计

查询:get
新增:post
修改:put
删除:delete


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/5/27 00:01
# @Author  : 杜兰特
# @File    : http_request_demo.py
from flask import Flask

app=Flask(__name__)

#methods是列表类型,可以添加多种请求方式。get、post、put、delete
@app.route("/cases",methods=['get'])
def get_case():
    return {"code":0,"msg":"get success"}

@app.route("/cases",methods=['post'])
def post_case():
    return {"code":0,"msg":"post success"}

@app.route("/cases",methods=['put'])
def put_case():
    return {"code":0,"msg":"put success"}

@app.route("/cases",methods=['delete'])
def delete_case():
    return {"code":0,"msg":"delete success"}

if __name__ == '__main__':
    app.run(debug=True)

四、请求与响应-处理请求数据

1、request的常用属性/方法

args:记录请求中的查询参数
json:记录请求中的json数据
files:记录请求上传的文件
form:记录请求中的表单数据
method:记录请求使用的http方法
url:记录请求的URL地址
host:记录请求的域名
headers:记录请求的头信息

2、get 请求参数

场景:普通的 url 链接,接收一个 get 请求
解决办法:request.args

#需要导入request,而不是requests!!!
from flask import Flask,request
from log_utils import logger

app=Flask(__name__)

@app.route('/login',methods=['get'])
def demo1():
    logger.info(f"请求参数为:{request.args}")
    a=request.args.get("a")
    b=request.args.get("b")
    logger.info(f"a的值为{a},b的值为{b}")
    return {"code":0,"msg":"get success"}

if __name__ == '__main__':
    app.run(debug=True)
127.0.0.1 - - [27/May/2023 09:03:29] "GET /login HTTP/1.1" 200 -
[2023-05-27 09:04:04] [INFO] [flask_param_demo.py]/[line: 17]/[demo1] 请求参数为:ImmutableMultiDict([('a', '88'), ('b', '66')]) 
[2023-05-27 09:04:04] [INFO] [flask_param_demo.py]/[line: 20]/[demo1] a的值为88,b的值为66 

3、json 请求

场景:POST 相关的请求,带有 json 数据格式
解决办法:request.json

#需要导入request,而不是requests!!!
from flask import Flask,request
from log_utils import logger

app=Flask(__name__)

@app.route('/register',methods=['post'])
def demo2():
    logger.info(f"请求参数为:{request.json}")
    data=request.json.get("time")
    logger.info(f"时间为{data}")
    return {"code":0,"msg":"post success"}

if __name__ == '__main__':
    app.run(debug=True)
[2023-05-27 09:05:55] [INFO] [flask_param_demo.py]/[line: 26]/[demo2] 请求参数为:{'time': '2023-05-27', 'status': 'success'} 
[2023-05-27 09:05:55] [INFO] [flask_param_demo.py]/[line: 28]/[demo2] 时间为2023-05-27 

4、表单请求

场景:比如:测试人网站的登录接口,需要用户名和密码,前端会提交一个 form 表单给后台
解决办法:request.form

#需要导入request,而不是requests!!!
from flask import Flask,request
from log_utils import logger

app=Flask(__name__)

@app.route('/register',methods=['put'])
def demo3():
    logger.info(f"请求参数为:{request.form}")
    name=request.form.get("name")
    password=request.form.get("password")
    email=request.form.get("email")
    logger.info(f"请求参数name为:{name},password为:{password},email为;{email}")
    return {"code": 0, "msg": "put success"}

if __name__ == '__main__':
    app.run(debug=True)
[2023-05-27 09:07:42] [INFO] [flask_param_demo.py]/[line: 34]/[demo3] 请求参数为:ImmutableMultiDict([('name', 'kobe'), ('password', '666666'), ('email', '77777')]) 
[2023-05-27 09:07:42] [INFO] [flask_param_demo.py]/[line: 38]/[demo3] 请求参数name为:kobe,password为:666666,email为;77777 

5、文件请求

场景:
页面上有个更新头像的功能, 或者上传一个 excel 文件的功能, 允许我们提交一个图片,或者文件到后端服务器,那么
解决方法:
request.files.get(‘file’) 获取文件对象
filename 获取文件对象的文件名
save()方法 保存文件到指定路径下
在这里插入图片描述

#需要导入request,而不是requests!!!
from flask import Flask,request
from log_utils import logger


app=Flask(__name__)

@app.route('/file',methods=['post'])
def demo4():
    logger.info(f"请求方法为:{request.method}")
    logger.info(f"请求url为:{request.url}")
    logger.info(f"请求host为:{request.host}")
    logger.info(f"请求headers为:{request.headers}")
    fileobj=request.files.get("name")
    logger.info(f"fileobj的值为:{fileobj}")
    logger.info(f"文件名为:{fileobj.filename}")
    #保存文件
    fileobj.save(f"./{fileobj.filename}")
    return {"code": 0, "msg": "file success"}

if __name__ == '__main__':
    app.run(debug=True)
[2023-05-27 09:10:10] [INFO] [flask_param_demo.py]/[line: 43]/[demo4] 请求方法为:POST 
[2023-05-27 09:10:10] [INFO] [flask_param_demo.py]/[line: 44]/[demo4] 请求url为:http://127.0.0.1:5000/file 
[2023-05-27 09:10:10] [INFO] [flask_param_demo.py]/[line: 45]/[demo4] 请求host为:127.0.0.1:5000 
[2023-05-27 09:10:10] [INFO] [flask_param_demo.py]/[line: 46]/[demo4] 请求headers为:User-Agent: PostmanRuntime/7.32.2
Accept: */*
Postman-Token: 593d536e-4bbd-45a2-89be-d8670f66aa42
Host: 127.0.0.1:5000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------------------------624583842625202752710421
Content-Length: 10016

 
[2023-05-27 09:10:10] [INFO] [flask_param_demo.py]/[line: 48]/[demo4] fileobj的值为:<FileStorage: '员工用车导入模板 (2).xlsx' ('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')> 
[2023-05-27 09:10:10] [INFO] [flask_param_demo.py]/[line: 49]/[demo4] 文件名为:员工用车导入模板 (2).xlsx 
127.0.0.1 - - [27/May/2023 09:10:10] "POST /file HTTP/1.1" 200 -

五、请求与响应-处理响应信息

1、文本型

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/5/27 09:27
# @Author  : 杜兰特
# @File    : http_response_demo.py

from flask import Flask
from flask import jsonify,render_template,make_response

app=Flask(__name__)

#返回文本
@app.route("/text")
def demo():
    return "文本信息"

if __name__ == '__main__':
    app.run(debug=True)

2、元组

返回元组
(response, status)
(response, headers)
(response, status, headers)
响应状态码默认为 200

from flask import Flask
from flask import jsonify,render_template,make_response

app=Flask(__name__)


#返回元组
@app.route("/tuple")
def tuple_res():
    return 'hello',200,{"status": 0, "type": "success"}


if __name__ == '__main__':
    #host
    #port
    #debug=True:热加载,方便调试
    app.run(debug=True)

在这里插入图片描述

3、Json

直接返回 dict 会转换为 json
使用jsonify()方法,通过参数传入键值对

from flask import Flask
from flask import jsonify,render_template,make_response

app=Flask(__name__)

#返回字典
@app.route("/dict")
def get_dict_res():
    return {"status":0}

@app.route("/json")
def get_json_res():
    #return jsonify({"status":0})
    #或者
    return jsonify(status=1,name="kobe")

if __name__ == '__main__':
    #host
    #port
    #debug=True:热加载,方便调试
    app.run(debug=True)

4、html

使用模板渲染技术
html 文件必须在同级的 templates 目录下

from flask import Flask
from flask import jsonify,render_template,make_response

app=Flask(__name__)


@app.route("/html")
def get_html():
    return render_template("demo.html")


if __name__ == '__main__':
    #host
    #port
    #debug=True:热加载,方便调试
    app.run(debug=True)

5、额外数据

设置额外数据-make_response()
添加更多的响应信息

设置 cookie
设置响应头信息等

from flask import Flask
from flask import jsonify,render_template,make_response

app=Flask(__name__)

@app.route("/")
def index():
    resp=make_response(render_template("demo.html"))
    #设置cookie
    resp.set_cookie("user","jimi")
    #设置响应头信息
    resp.headers["type"]="kobe"
    return resp

if __name__ == '__main__':
    #host
    #port
    #debug=True:热加载,方便调试
    app.run(debug=True)

在这里插入图片描述
在这里插入图片描述

六、测试平台环境配置

监听的主机

设置 host 参数
127.0.0.1 只能本机访问
0.0.0.0 服务发布到局域网

app.run(host="0.0.0.0")

监听的端口
设置 port 参数(默认端口号 5000)

app.run(host="0.0.0.0", port=5000)

Debug 模式
设置 debug=True(默认是 production)

实现热加载
开发调试方便

app.run(host="0.0.0.0", port=5000,debug=True)

七、接口配置

1、flask-restx 介绍

官方文档:https://github.com/python-restx/flask-restx
flask-restx 是一个支持 RESTFUL 的 flask 插件,用于规范化接口的编写,并且支持 swagger 文档。

2、插件安装

pip install flask-restx

3、demo 示例

from flask import Flask
#from flask_restful import Resource,Api
from flask_restx import Resource,Api

app=Flask(__name__)

#1、创建api实例对象
api=Api(app)

#2、使用api添加路由
@api.route('/hello')
class HelloWorld(Resource):     #3、类要继承Resource
    #4、定义restful风格的get方法
    def get(self):
        return {'hello':'world'}

    # restful风格的post方法
    def post(self):
        return {'post':'success'}

if __name__ == '__main__':
    app.run(debug=True)

在这里插入图片描述

八、接口使用

1、设计框架的原则

复用性
高内聚,低藕合

a、高耦合实例

问题:判断条件过多,业务逻辑非常复杂

@app.route("/testcase",methods=["GET","PUT","POST"])
def select_case():
    ### request:Request
    if request.method == "GET":
        pass
    elif request.method == "PUT":
        pass

b、低内聚示例

问题:同一个路径,对应多个请求方法,代码没有复用

# get 请求
@app.route("/testcase",methods=["get"])
def select_case():
    # request:Request
    # m = request.method
    print(dir(request))
    return {"code": 0, "msg":"get success"}

# post 请求
@app.route("/testcase",methods=["post"])
def post_case():
    return {"code": 0, "msg":"post success"}

# delete 请求
@app.route("/testcase",methods=["delete"])
def delete_case():
    return {"code": 0, "msg":"delete success"}


# put 请求
@app.route("/testcase",methods=["put"])
def put_case():
    return {"code": 0, "msg":"put success"}

2、编写 RESTFUL 风格的接口

导入from flask_restx import Resource,Api
app=Flask(name) 创建app实例对象
api=Api(app) 创建api实例对象
使用api添加路由
类要继承Resource
定义restful风格的方法

from flask import Flask
#from flask_restful import Resource,Api
from flask_restx import Resource,Api

app=Flask(__name__)

#1、创建api实例对象
api=Api(app)

#2、使用api添加路由
#@api.route('/user','/user1','/user2')  添加多个路由
@api.route('/user')
class User(Resource):     #3、类要继承Resource
    #4、定义restful风格的get方法
    def get(self,id):
        return {"id":id,'code':0,"msg":"get success"}

    # restful风格的post方法
    def post(self):
        return {'code':0,"msg":"post success"}

    # restful风格的put方法
    def put(self):
        return {'code':0,"msg":"put success"}

    # restful风格的delete方法
    def delete(self):
        return {'code':0,"msg":"delete success"}


api.add_resource(User,'/user/<int:id>','/user_1')

if __name__ == '__main__':
    app.run(debug=True)

3、添加路由的方式

方式一:

添加多个路由
@api.route('/user','/user1','/user2')

@api.route('/user','/user1','/user2')  添加多个路由
@api.route('/user')
class User(Resource):

方式二

api.add_resource(User,'/user/<int:id>','/user_1')

九、集成swagger

1、namespace 的使用

定义 Namespace 实例
为类添加装饰器 @namespace.route(“”) 控制子路由
为命名空间指定访问资源路径 api.add_namespace(case_ns, ‘/case’)

from flask import Flask
from flask_restx import Api,Resource,Namespace

app=Flask(__name__)
api=Api(app)

#定义了2个命名空间
hello_ns=Namespace("demo",description="demo学习")
case_ns=Namespace("case",description="用例管理")

#将api.route("case")  改为:case_ns.route("/case")
#@case_ns.route("") 定义子路由,如果没有的话,传空字符串即可
@case_ns.route("")
class TestCase(Resource):

    def get(self):
        pass

    def post(self):
        pass

    def put(self):
        pass

    def delete(self):
        pass


@hello_ns.route("/demo")	#子路由
class Demo(Resource):

    def get(self):
        pass

    def post(self):
        pass

    def put(self):
        pass

    def delete(self):
        pass

api.add_namespace(hello_ns,"/hello")
api.add_namespace(case_ns,"/case")

if __name__ == '__main__':
    app.run(debug=True)

2、swagger 接口文档配置

定义参数 parser = api.parser()
传递及校验参数 @api.expect(parser) 或者 @namespace.expect(parser)

a、api.parser()用法

格式:api.parser().add_argument(参数名, 关键字参数)
第一个参数是参数名
后面是关键字传参,常用的关键字有:
type :类型
required 约束控制
choices 枚举参数
location 对应 request 对象中的属性

参数名参数值
typeint,bool,float,string,FileStorage
requiredTrue/False
choices枚举
locationargs,form,json,files

b、get 请求示例

from flask import Flask, request
from flask_restx import Resource, Api, Namespace,fields
from log_utils import logger
from werkzeug.datastructures import FileStorage

app = Flask(__name__)
api = Api(app)
hello_ns = Namespace("demo", description="demo学习")

@hello_ns.route("")
class Demo(Resource):
    #定义parser解析器对象
    get_parser = api.parser()
    #通过parser对象添加测试参数
    get_parser.add_argument('id',
                            type=int,
                            location="args",
                            required=True)

    get_parser.add_argument('case_title',
                            type=str,
                            location="args",
                            required=True)

    @hello_ns.expect(get_parser)
    def get(self):
        logger.info(f"request.args ===>{request.args}")
        return {"code": 0, "msg": "get success"}


api.add_namespace(hello_ns,"/hello")

if __name__ == '__main__':
    app.run(debug=True)

接口文档如下

在这里插入图片描述

c、post 请求示例

1、处理 json 格式
from flask import Flask, request
from flask_restx import Resource, Api, Namespace,fields
from log_utils import logger
from werkzeug.datastructures import FileStorage

app = Flask(__name__)
api = Api(app)
hello_ns = Namespace("demo", description="demo学习")

@hello_ns.route("")
class Demo(Resource):
    
    post_parser=api.parser()
    post_parser.add_argument("id",type=int,location="json",required=True)
    post_parser.add_argument("casetitle",type=str,location="json",required=True)


    @hello_ns.expect(post_parser)
    def post(self):
        logger.info(f"request.json ===>{request.json}")
        return {"code": 0, "msg": "post success"}


api.add_namespace(hello_ns,"/hello")

if __name__ == '__main__':
    app.run(debug=True)

在这里插入图片描述

2、处理 files格式
from flask import Flask, request
from flask_restx import Resource, Api, Namespace,fields
from log_utils import logger
from werkzeug.datastructures import FileStorage

app = Flask(__name__)
api = Api(app)
hello_ns = Namespace("demo", description="demo学习")

@hello_ns.route("")
class Demo(Resource):
    
    post_parser=api.parser()
    post_parser.add_argument("file",type=FileStorage,location="files",required=True)

    @hello_ns.expect(post_parser)
    def post(self):
        logger.info(f"request.files ===>{request.files}")
        return {"code": 0, "msg": "post success"}


api.add_namespace(hello_ns,"/hello")

if __name__ == '__main__':
    app.run(debug=True)

在这里插入图片描述
请求接口
在这里插入图片描述

3、处理 form格式
from flask import Flask, request
from flask_restx import Resource, Api, Namespace,fields
from log_utils import logger
from werkzeug.datastructures import FileStorage

app = Flask(__name__)
api = Api(app)
hello_ns = Namespace("demo", description="demo学习")

@hello_ns.route("")
class Demo(Resource):
    
    post_parser=api.parser()
    post_parser.add_argument("param1",help="username",type=int,location="form",required=True)
    post_parser.add_argument("param2",help="password",type=int,location="form",required=True)

    @hello_ns.expect(post_parser)
    def post(self):
        logger.info(f"request.form ===>{request.form}")
        return {"code": 0, "msg": "post success"}


api.add_namespace(hello_ns,"/hello")

if __name__ == '__main__':
    app.run(debug=True)

在这里插入图片描述

[2023-05-28 22:04:04] [INFO] [swagger_demo2.py]/[line: 53]/[post] request.form ===>ImmutableMultiDict([('param1', '24'), ('param2', '123567')]) 
127.0.0.1 - - [28/May/2023 22:04:04] "POST /hello HTTP/1.1" 200 -
4、处理choice格式
from flask import Flask, request
from flask_restx import Resource, Api, Namespace,fields
from log_utils import logger
from werkzeug.datastructures import FileStorage

app = Flask(__name__)
api = Api(app)
hello_ns = Namespace("demo", description="demo学习")

@hello_ns.route("")
class Demo(Resource):
    
    post_parser=api.parser()
    post_parser.add_argument("choice", location="args", required=True,choices=(1,2,3,4))

    @hello_ns.expect(post_parser)
    def post(self):
        logger.info(f"request.args ===>{request.args}")
        return {"code": 0, "msg": "post success"}


api.add_namespace(hello_ns,"/hello")

if __name__ == '__main__':
    app.run(debug=True)
[2023-05-28 22:06:14] [INFO] [swagger_demo2.py]/[line: 54]/[post] request.args ===>ImmutableMultiDict([('choice', '2')]) 
127.0.0.1 - - [28/May/2023 22:06:14] "POST /hello?choice=2 HTTP/1.1" 200 -

十、orm介绍

1、什么是持久化

持久化(Persistence) 即把数据保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的数据存储在关系型的数据库中。当然也可以存储在磁盘文件中、XML数据文件中等等。

2、什么是 ORM

Object Relational Mapping 对象关系映射
作用是在关系型数据库和对象之间做一个映射,这样在具体操作数据库的时候,就不需要再去和复杂的 SQL 语句打交道,只要像平时操作对象一样操作就可以了
在这里插入图片描述

3、ORM 与 SQL 的对比

# 使用sql语句查询数据
sql = "SELECT
username, email, gender
FROM User  WHERE id = 1"

# 执行sql 代码
res = db.execSql(sql);

# 取出数据
name = res[0]
# 使用orm 查询数据,定义ORM对象,get 值 ,
res = db.session.query(\
    User.username, User.email, User.gender).\
    filter(User.id==1).first()
name = res.username

在这里插入图片描述

4、ORM 优缺点

优势:

隐藏了数据访问细节
ORM 使我们构造固化数据结构变得非常简单

缺点:

性能下降 ,添加了关联操作,性能不可避免的会下降一些
无法解决特别复杂的数据库操作

十一、orm中间件配置

from flask import Flask
# 实例化 Flask的类,并且绑定module
from flask_sqlalchemy import SQLAlchemy
# 实例化 Flask
app=Flask(__name__)
# mysql 数据库用户名
username="root"
# mysql 数据库密码
pwd="root"
# mysql 数据库的 host 地址
ip="127.0.0.1"
# mysql 数据库端口
port="3306"
# 代码使用的数据库名
datebase="demo"
# 设置mysql 链接方法是
app.config['SQLALCHEMY_DATABASE_URL']=f"mysql+pymysql://{username}:{pwd}@{ip}:{port}/{database}?charset=utf-8"
# 定义应用使用数据库的配置
# 设置SQLALCHEMY_TRACK_MODIFICATIONS参数 不设置该配置的时候会抛出警告
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=True
# 将 app 与 Flask-SQLAlchemy 的 db 进行绑定
db=SQLAlchemy(app)

十二、数据库与表管理

Flask-SQLAlchemy 数据库连接的配置

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://username:password@host:port/database'

database:
  username: root
  password: root
  server: 127.0.0.1:3306
  db: flask_1

定义数据库的表 需要继承 db.Model,db 为 app 启动的时的 SQLAlchemy 绑定的实例
类名,相当于表名
驼峰命名的类名,转为下划线连接。例如 class UserInfo 建立的表名为 user_info
自定义表名,__tablename__= 自定义的表名

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/5/28 22:37
# @Author  : 杜兰特
# @File    : sqlachemy_demo.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
# 实例化app 对象
from sqlalchemy import *
import yaml

app = Flask(__name__)
with open("./data.yaml") as f :
    result = yaml.safe_load(f)
    username = result.get("database").get('username')
    password = result.get("database").get('password')
    server = result.get("database").get('server')
    db = result.get("database").get('db')
app.config['SQLALCHEMY_DATABASE_URI'] = \
    f"mysql+pymysql://{username}:{password}@{server}/{db}?charset=utf8"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# SQLAlchemy 绑定app
db = SQLAlchemy(app)

# 定义数据库的表 需要继承 db.Model,db 为 app 启动的时的 SQLAlchemy 绑定的实例

# 类名,相当于表名
#驼峰命名的类名,转为下划线连接。例如 class UserInfo 建立的表名为 user_info
#自定义表名,__tablename__= 自定义的表名
class User(db.Model):
    __tablename__="User"    #指定表名
    id = Column(Integer, primary_key=True)
    username = Column(String(80))

class StudentInfo(db.Model):
    id = Column(Integer, primary_key=True)
    username = Column(String(80))

if __name__ == '__main__' :
    #可以创建多个表
    db.create_all()

    #db.drop_all()

创建表操作

可以创建多个表
db.create_all()

删除表操作

db.drop_all()

十三、对象与数据模型

1、数据模型

数据模型(Data Model)是数据特征的抽象,它从抽象层次上描述了系统的静态特征、动态行为和约束条件,为数据库系统的信息表示与操作提供一个抽象的框架。

2、设计用户表字段

在这里插入图片描述

3、Flask-SQLAlchemy 的属性字段定义

通常类的属性相当于表的一个字段
定义的属性的方式为 name=Column(参数的类型, 其他的属性)
官方:https://flask-sqlalchemy.palletsprojects.com/en/2.x/models/?highlight=column

参数类型含义
Integer整型字段定义
String(20)字符串字段定义,括号为字符串的最大长度
JSONjson 字符串字段
DateTime时间格式字段

4、Flask-SQLAlchemy 字段常用关键字参数

参数类型含义
primary_key是否主键
autoincrement是否自增
nullable是否允许为空
unique是否允许重复
default默认值

5、Flask-SQLAlchemy 对象与数据模型示例

每一个类变量表示一个数据库表的列名
第一个参数是表示数据的类型, primary_key=True 表示是主键
unique = True 表示是不能重复的
nullable=False 表示不可以为空

from sqlalchemy import *
from flask_sqlalchemy import SQLAlchemy
import yaml
# 导入 Query 以便于调用的时候代码提示
from sqlalchemy.orm import Query
from flask import Flask
app = Flask(__name__)

with open("./data.yaml") as f :
    result = yaml.safe_load(f)
    username = result.get("database").get('username')
    password = result.get("database").get('password')
    server = result.get("database").get('server')
    db = result.get("database").get('db')
app.config['SQLALCHEMY_DATABASE_URI'] = \
    f"mysql+pymysql://{username}:{password}@{server}/{db}?charset=utf8"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# SQLAlchemy 绑定app
db = SQLAlchemy(app)

# 定义数据库的表 需要继承 db.Model
class User(db.Model):
    __tablename__ = "user" # 设置数据库表名
    # 每一个类变量表示一个数据库表的列名
    # 第一个参数是表示数据的类型, primary_key=True 表示是主键
    id = Column(Integer, primary_key=True)
    # unique = True 表示是不能重复的  nullable=False 表示不可以为空
    username = Column(String(80), unique=False, nullable=False)
    email = Column(String(120), unique=True, nullable=False)
    gender = Column(String(3),unique=False)

    def __repr__(self):
        # 数据库的 魔法方法 直观展示数据
        '''[<User "张三">,<User "李四">]'''
        return f'<User {self.username}>'

十四、数据CRUD

1、添加数据(create)

a、单条数据新增

实例化类,创建表数据
将实例添加到 session(add)
提交更新 (commit)
关闭 session

# 新增表数据,需要导入 User 类,进行实例化
user=User(id=1,username="kobe",email="111@qq.com",gender="男")
print(user)
db.session.add(user)
db.session.commit()
db.session.close()

b、多条数据新增

多次实例化类,创建多条表数据
将多个实例依次添加到 session 中(add)或者一次性添加到 session 中(add_all)
提交更新 (commit)
关闭 session

# 批量添加数据操作
user1 = User(username="kobe1", email="1113@qq.com", gender="男")
user2 = User(username="kobe2", email="1114@qq.com", gender="女")
db.session.add_all([user1,user2])
db.session.commit()
db.session.close()

2、读取数据(read)

a、查询表中全部数据

格式:类.query.all()

# 读取全部数据
res = User.query.all()
# 遍历数据,得到想要的字段
for rs in res:
    print(rs.username, rs.email)

b、条件查询——单条件查询

格式:类.query.filter_by(条件).单条或多条

查询单条数据:类.query.filter_by(条件).first()

res=User.query.filter_by(gender='男').first()
print(res)

查询多条数据:类.query.filter_by(条件).all()

res_list=User.query.filter_by(gender="男").all()
print(res_list)

c、条件查询——多条件查询

查询单条数据:类.query.filter_by(条件).filter_by(条件)…first()

res1=User.query.filter_by(gender="男").filter_by(username="kobe1").first()
print(res1)

查询多条数据:类.query.filter_by(条件).filter_by(条件)…all()

res2 = User.query.filter_by(gender="男").filter_by(username="kobe1").all()
print(res2)

3、修改数据(update)

方式一:
首先查询出来需要的数据
对查询出来的数据对象进行属性的修改
提交 session

user3=User.query.filter_by(id=2).first()
user3.gender="女"
db.session.commit()
db.session.close()

方式二:
给定查询条件进行查询后,直接进行 update 操作
提交 session

res4=User.query.filter_by(id=3).update({"username":"butler"})
db.session.commit()
db.session.close()

4、删除数据(delete)

方式一:
查询数据
对查询出来的数据对象进行删除操作
提交 session

user5=User.query.filter_by(id=1).first()
db.session.delete(user5)
db.session.commit()
db.session.close()

方式二:
给定查询条件进行查询后,直接进行 delete 操作
提交 session

user5=User.query.filter_by(id=2).delete()
db.session.commit()
db.session.close()

十五、多表关系-一对多

1、一对多关系

场景:
一个班级有多个学生
一个学生只在一个班级
一对多关系中,通过外键来关联数据,外键设置在多的一方

SQLAlchemy提供了一个relationship,这个类可以定义属性,以后在访问相关联的表的时候就直接可以通过属性访问的方式就可以访问得到了。另外,可以通过backref来指定反向访问的属性名称。

特别注意:
classinfo=db.relationship(‘ClassInfo’,backref=“studentinfo”):代表学生所属的班级信息,backref:反向关联的属性名,classinfo=db.relationship(‘ClassInfo’,backref=“studentinfo”):中的ClassInfo必须是另外一个相关联的类名

from sqlalchemy import *
from flask_sqlalchemy import SQLAlchemy
import yaml
# 导入 Query 以便于调用的时候代码提示
from sqlalchemy.orm import Query
from flask import Flask
app = Flask(__name__)

with open("../data.yaml") as f :
    result = yaml.safe_load(f)
    username = result.get("database").get('username')
    password = result.get("database").get('password')
    server = result.get("database").get('server')
    db = result.get("database").get('db')
app.config['SQLALCHEMY_DATABASE_URI'] = \
    f"mysql+pymysql://{username}:{password}@{server}/{db}?charset=utf8"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# SQLAlchemy 绑定app
db = SQLAlchemy(app)


# 班级表
class ClassInfo(db.Model):
    __tablename__ = "Class"
    id = Column(Integer, primary_key=True)
    name = Column(String(80))

    def __repr__(self):
        return f"<Class:{self.name}>"

# 学生表
class StudentInfo(db.Model):
    # 指定表名__tablename__属性
    __tablename__ = "Student"
    id = Column(Integer, primary_key=True)
    name = Column(String(80))
    #添加外键
    classid = Column(Integer, ForeignKey("Class.id"))

    # 第一个参数:表示这个关系的另一端是 ClassInfo 类。比如班级id=1
    # 第二个参数:backref, 表示反向引用,需要从班级id为1 反向获取【多】的时候,使用的属性
    # 通过ClassInfo实例.studentinfo
    classinfo=db.relationship('ClassInfo',backref="studentinfo")

    def __repr__(self):
        return f"<Student:{self.name}>"

2、一对多表结构

创建表
db.create_all()
删除表
db.drop_all()

3、一对多增删查改

a、数据新增

Class 班级表:添加两条数据

class1 = ClassInfo(id=1, name="测开21期")
class2 = ClassInfo(id=2, name="测开22期")
db.session.add_all([class1, class2])
db.session.commit()
db.session.close()

要先完全创建好,数据库中的班级表,再去创建学生表
Student 学生表:添加两条数据

student1 = StudentInfo(id=1, name="学生一", classid=1)
student2 = StudentInfo(id=2, name="学生二", classid=1)
student3 = StudentInfo(id=3, name="学生三", classid=2)
student4 = StudentInfo(id=4, name="学生四", classid=2)
db.session.add_all([student1, student2,student3, student4])
db.session.commit()
db.session.close()

b、数据查询

多查一

通过学生id=1 查询学生所在的班级

stu1=StudentInfo.query.filter_by(id=1).first()
#获取classid信息
print(stu1.classid)
print(stu1.classinfo)
print(stu1.classinfo.name)
一查多

通过班级id=1,查询对应的学生

class1=ClassInfo.query.filter_by(id=1).first()
print(class1.studentinfo)
print(class1.studentinfo[0].name)

c、数据修改

一改多

通过班级修改学生信息

class1=ClassInfo.query.filter_by(id=1).first()
print(class1.studentinfo)
class1.studentinfo[0].name="学生修改1"
db.session.commit()
db.session.close()
多改一

通过学生修改班级信息

stu2=StudentInfo.query.filter_by(id=2).first()
print(stu2.classinfo)
stu2.classinfo.name="pythonvip"
db.session.commit()
db.session.close()

d、数据删除

删除一个班级下的所有学生

class1=ClassInfo.query.filter_by(id=1).first()
StudentInfo.query.filter(StudentInfo.classid==class1.id).delete()
db.session.commit()
db.session.close()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1159500.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高效文件整理:按数量划分自动建立文件夹,轻松管理海量文件

在日常生活和工作中&#xff0c;我们经常需要处理大量的文件。然而&#xff0c;如何高效地整理这些文件却是一个棘手的问题。有时候&#xff0c;我们可能需要按照特定的规则来建立文件夹&#xff0c;以便更高效地整理文件。例如&#xff0c;您可以按照日期、时间或者特定的标签…

老杨说运维 | 历时180天,跟复旦大学共研的运维大模型终于来了!

写在前面 Q1&#xff1a;到处都在说的AI大模型到底是什么? ? ? A1&#xff1a;AI大模型是“人工智能预训练大模型"的简称&#xff0c;它包含了"预训练“和”大模型“两层含义&#xff0c;二者结合产生了一种新的人工智能模式即模型在大规模数据集上完成了预训练…

Azure 机器学习 - 使用无代码 AutoML 训练分类模型

了解如何在 Azure 机器学习工作室中使用 Azure 机器学习自动化 ML&#xff0c;通过无代码 AutoML 来训练分类模型。 此分类模型预测某个金融机构的客户是否会认购定期存款产品。 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管…

【C语法学习】5 - fputc()函数

文章目录 1 函数原型2 参数3 返回值4 示例4.1 示例14.2 示例24.3 示例3 1 函数原型 fputc()&#xff1a;将一个字符发送至指定流stream&#xff0c;函数原型如下&#xff1a; int fputc(int c, FILE *stream);2 参数 fputc()函数有两个参数c和stream&#xff1a; 参数c是待…

Proteus仿真--基于51单片机的按键控制LED仿真(仿真文件+程序)

本文主要介绍基于51单片机的按键控制LED仿真&#xff08;完整仿真源文件及代码见文末链接&#xff09; 本仿真文件主要涉及4个按键&#xff0c;其中&#xff1a; K1按键的逻辑是——逐个点亮 K2按键的逻辑是——上四个点亮 K3按键的逻辑是——下四个点亮 K4按键的逻辑是——关…

opencv复习(很乱)

2-高斯与中值滤波_哔哩哔哩_bilibili 1、均值滤波 2、高斯滤波 3、中值滤波 4、腐蚀操作 卷积核不都是255就腐蚀掉 5、膨胀操作 6、开运算 先腐蚀再膨胀 7、闭运算 先膨胀再腐蚀 8、礼帽 原始数据-开运算结果 9、黑帽 闭运算结果-原始数据 10、Sobel算子 左-右&#x…

⾯向对象编程:封装数据和⾏为、定义交互协议、扩展与复⽤ - GO语言从入门到实战

⾯向对象编程&#xff1a;封装数据和⾏为、定义交互协议、扩展与复⽤ - GO语言从入门到实战 一、封装数据和⾏为 结构体定义 定义了一个名为Structural的结构体。结构体是一种用户自定义的数据类型&#xff0c;可以包含不同类型的字段&#xff08;成员变量&#xff09;。 与…

【Unity编辑器扩展】艺术字/自定义图片字体生成工具

艺术字在游戏中很常用&#xff0c;由于普通字体样式过于平淡&#xff0c;制作花里胡哨的文字图片作为游戏字体使用&#xff0c;这就是艺术字。 不依赖第三方工具&#xff0c;仅使用Unity自带的Custom Font 一张艺术字图集就能实现这个功能&#xff0c;但是为了便于使用&#…

多伦多公共图书馆遭遇周末网络攻击,服务中断

多伦多公共图书馆&#xff08;TPL&#xff09;在10月28日星期六遭遇网络攻击后&#xff0c;警告称其许多在线服务已经中断。 作为加拿大最大的公共图书馆系统&#xff0c;TPL通过多伦多市内的100个分馆为人们提供1200万本图书的借阅服务。图书馆拥有120万注册会员&#xff0c;…

[SpringCloud | Linux] CentOS7 部署 SpringCloud 微服务

目录 一、环境准备 1、工具准备 2、虚拟机环境 3、Docker 环境 二、项目准备 1、配置各个模块&#xff08;微服务&#xff09;的 Dockerfile 2、配置 docker-compose.yml 文件 3、Maven 打包 4、文件整合并传输 三、微服务部署 1、部署至 Docker 2、访问微服务 四…

【Verilog】7.2.1 Verilog 并行 FIR 滤波器设计

FIR&#xff08;Finite Impulse Response&#xff09;滤波器是一种有限长单位冲激响应滤波器&#xff0c;又称为非递归型滤波器。 FIR 滤波器具有严格的线性相频特性&#xff0c;同时其单位响应是有限长的&#xff0c;因而是稳定的系统&#xff0c;在数字通信、图像处理等领域…

MacOS安装git

文章目录 通过Xcode Command Lines Tool安装(推荐)终端直接运行git命令根据流程安装先安装Command Lines Tool后再安装git 官网下载二进制文件进行安装官方国外源下载二进制文件(不推荐)国内镜像下载二进制文件(推荐)安装git 通过Xcode Command Lines Tool安装(推荐) 简单来讲C…

性能压力测试主要目标及步骤

性能压力测试是软件开发生命周期中至关重要的一部分&#xff0c;旨在评估应用程序或系统在高负载和极端条件下的性能表现。这种测试有助于发现性能瓶颈、资源耗尽和错误&#xff0c;以确保应用程序在真实使用情况下的可靠性和稳定性。本文将探讨性能压力测试的概念、方法和最佳…

Distribution-Aware Coordinate Representation for Human Pose Estimation阅读笔记

主要研究人体姿态估计中heatmap转坐标的方法&#xff0c;提出一种新的解码方法 &#xff08;其实这人体姿态我毛也不会&#xff0c;过来看看这个heatmap解码方法&#xff09; 代码&#xff1a;https://github.com/ilovepose/DarkPose/blob/master/lib/core/inference.py 方法…

保障效率与可用,分析Kafka的消费者组与Rebalance机制

系列文章目录 上手第一关&#xff0c;手把手教你安装kafka与可视化工具kafka-eagle Kafka是什么&#xff0c;以及如何使用SpringBoot对接Kafka 架构必备能力——kafka的选型对比及应用场景 Kafka存取原理与实现分析&#xff0c;打破面试难关 防止消息丢失与消息重复——Kafka可…

YOLOv5 分类模型的预处理

YOLOv5 分类模型的预处理 flyfish 版本 6.2 将整个代码简化成如下代码 imgsz224 file "/home/a/Pictures/1.jpg" transforms classify_transforms(imgsz) im cv2.cvtColor(cv2.imread(file), cv2.COLOR_BGR2RGB) print(im.shape)im transforms(im) print(im.…

【计算机网络】第二章:应用层

应用层协议原理 客户-服务器体系结构&#xff1a; 特点&#xff1a;客户之间不能直接通信&#xff1b;服务器具有周知的&#xff0c;固定的地址&#xff0c;该地址称为IP地址。 配备大量主机的数据中心常被用于创建强大的虚拟服务器&#xff1b;P2P体系结构&#xff1a; 特点&…

项目压测优化

基本信息 客户名称&#xff1a;xxx 产品名称&#xff1a;ATS 版本号&#xff1a;版本无关 问题分类&#xff1a;性能问题 问题描述 压测付款查询和收款查询接口&#xff0c;发现cpu过高&#xff0c;响应时间过长不符合要求。 客户要求&#xff1a;1500并发情况下&#xff0c;接…

BUUCTF 后门查杀 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 小白的网站被小黑攻击了&#xff0c;并且上传了Webshell&#xff0c;你能帮小白找到这个后门么&#xff1f;(Webshell中的密码(md5)即为答案)。 密文&#xff1a; 下载附件&#xff0c;解压得到一个网站文件夹。 解…

呼吸灯【FPGA】

晶振50Mhz 1us 等于 计0~49 1ms等于 0~999us 1s等于 0~999ms //led_outalways(posedge FPGA_CLK_50M_b5 or negedge reset_e8) //【死循环】敏感【触发条件&#xff1a;上升沿 clk】【运行副本】if(reset_e81b0)begin //50Mhz晶振&#xff0c; 49_999_999 是 1秒…