29_python笔记-flask框架

文章目录

  • 配置MySQL Workbench
  • Git仓库
  • 创建虚拟环境
  • 配置解释器
  • 创建项目
  • 路由查找
  • 使用Postman发送请求
  • 提交到Git
  • 配置文件的拆分
  • 核心对象拆分
  • 视图文件注册到蓝图
  • 整合router的注册
  • 蓝图注册到app
  • json数据的展示
  • 新闻资讯站点示例
  • Flask数据模型
  • ORM
  • sqlite本地文件数据库(本地测试使用)
  • 数据模型定义方法2
  • Flask命令行
  • 使用MySQL Workbench 插入语句
  • 数据库增删查改
    • 查询
    • 增加
    • 修改
    • 删除
  • api 统一标准返回
  • flask_marshmallow模块 序列化
  • mvc、mtv模式、前后端分离
  • RestFul API 表现层状态转化
  • 自带的序列化工具
  • 参数解析
  • CMDB
  • CMDB数据模型建立
    • 一对多关系
    • 多对多关系
    • 一对一关系
  • 获取服务器数据接口
  • 嵌套蓝图
  • 调用接口获取服务器数据
  • pycham 编辑远程脚本
  • 优化:增加API授权

个人博客
https://blog.csdn.net/cPen_web

配置MySQL Workbench

29_python笔记-flask框架_第1张图片
create database flask_app;
29_python笔记-flask框架_第2张图片
linux mysql 下 设置其他主机可以连接

UPDATE mysql.user SET Host='%' WHERE Host='localhost' AND User='root';
FLUSH PRIVILEGES;

Git仓库

·创建git仓库
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app
$ pwd
/e/web_cpen/flask_proj/flask_app
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app
$ git init				#注:初始化git仓库(把它变成git仓库)
Initialized empty Git repository in E:/web_cpen/flask_proj/flask_app/.git/
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
·添加.gitignroe配置(venv、logs等目录)
$ vim .gitignore		#注:文件不上传
*.pyc
.idea/
*.log
·提交一个版本(初始化完成)
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ git add --all
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ git commit -m "add ignore"
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ git remote -v			#注:没有远程仓库连接
#步骤:在码云上创建新仓库cpen_flask_app
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ git config --global user.name "cPen"
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ git config --global user.email "[email protected]"
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ git remote add origin https://gitee.com/cPen_web/cpen_flask_app.git
#注:把本地推送到远端+添加别名
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ git push origin master

创建虚拟环境

·创建虚拟环境(使用git-bash)
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj
$ python -m venv venv			#注:创建虚拟环境
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj
$ source venv/Scripts/activate	#注:进入虚拟环境
(venv)
·安装Flask
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj
$ pip install Flask				#注:安装Flask
(venv)

配置解释器

29_python笔记-flask框架_第3张图片
29_python笔记-flask框架_第4张图片
点ok
29_python笔记-flask框架_第5张图片
虚拟环境下(venv)

flask 基于werkzeug工具箱和jinja2模板引擎开发的一个轻量级web框架

创建项目

#创建第一个flask项目
from flask import Flask

app = Flask(__name__)   #实例化对象

app.run()
#创建第一个flask项目
#flask 基于werkzeug工具箱和jinja2模板引擎开发的一个轻量级web框架

#导入flask的核心对象
from flask import Flask

#实例化对象,传入一个名字,一般就用__name__
app = Flask(__name__)

#默认endpoint就是函数名,index
#method指定请求方法类型 默认为GET
#@app.route("/")
@app.route("/", methods=["POST","GET"]) #注:支持POST、GET请求,传入列表
def index():
    return "this is ok"

#设置路由
#@app.route("/index")
#def index2(username):
def index2():
    #return "this is index"
#return f"args {username} is ok"
    return f"args is ok"

@app.route("/index3")
def index3():
    return "hello world"

#app.add_url_rule("/index", view_func=index2) #第二种添加路由的方法
#app.add_url_rule("/index", view_func=index2, endpoint="index3")
#构造动态url,让url传递参数
#app.add_url_rule("/index/", view_func=index2) #注:传入参数
#app.add_url_rule("/index/", view_func=index2,methods=["POST"])
app.add_url_rule("/index/", view_func=index2,methods=["POST"])
#注:支持POST请求
#注:用于学生管理系统,在url里传递id; 删除修改操作

#打开调试模式
#app.debug = True
#app.run()
app.run(host="0.0.0.0",port=5555,debug=True) #注:第二种方式 调试模式+绑定5555端口,主机

#点击结果为 this is ok
#点击结果为 this is index
#127.0.0.1 - - [11/Dec/2020 16:46:20] "GET / HTTP/1.1" 200 -
#------------------------------------------------------------
#127.0.0.1:5000/index/cp
#args cp is ok

#注:默认GET请求
#注:405 请求的方式不对 405 Method Not Allowed

路由查找

#flask会维护两张表
#一张表记录 从url到endpoint的映射关系   url_map
#一张表记录 从endpoint到func的映射关系  view_functions

#endpoint在flask路由表中具有唯一性
#endpoint没有指定,默认就是函数名

使用Postman发送请求

29_python笔记-flask框架_第6张图片

提交到Git

15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj
$ cd flask_app/
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ git add server.py
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ git commit -m "flask_01"
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ git push origin master

配置文件的拆分

conf目录 --> settings.py文件
Host = "0.0.0.0"
Port = 8000
Debug = True
HOST = "0.0.0.0"
PORT = 8000
DEBUG = True

方法1:
server.py文件

from conf.settings import *
app.run(host=Host,port=Port,debug=Debug)

方法2:
server.py文件

app.config.from_object('conf.settings')

app.run(host=app.config["HOST"],
        port=app.config["PORT"],
        debug=app.config["DEBUG"])

核心对象拆分

app.py文件

import os
from flask import Flask

print(os.environ)

def create_app(config=None):
    app = Flask(__name__)
    # load default configuration
    app.config.from_object('conf.settings')
    # app.config.from_object('config.secure')

    # load environment configuration
    # FLASK_CONF="/path/to/config_dev.py"
    # FLASK_CONF="/path/to/config_prod.py"
    # 也可以根据系统环境变量,加载不同的配置文件
    if 'FLASK_CONF' in os.environ:
        app.config.from_envvar('FLASK_CONF')

    # load app sepcified configuration
    if config is not None:
        if isinstance(config, dict):
            app.config.update(config)
        elif config.endswith('.py'):
            app.config.from_pyfile(config)
    return app

server.py文件

from app import create_app

app = create_app()

router目录 --> view01.py文件

#注:(这种写法不好,router目录下 多个文件不符合美感,引入下面的蓝图)
from app import create_app
app = create_app()

@app.route("/", methods=["POST","GET"])
def index():
    return "this is ok"

# @app.route("/index")
def index2():
    return f"args is ok"

@app.route("/index3")
def index3():
    return "hello world"
#构造动态url,让url传递参数
app.add_url_rule("/index/", view_func=index2,methods=["POST"])

server.py文件

from router.view01 import app

环境变量

>>> import os
>>> print(os.environ)		#注:Python里查看系统环境变量
[root@localhost ~]# env		#注:linux里查看环境变量

Linux里添加(定义)环境变量
[root@localhost ~]# vim .bashrc 
FLASK_CONF=/home/sanchuang/flask_config.py
export FLASK_CONF
[root@localhost ~]# vim /home/sanchuang/flask_config.py
[root@localhost ~]# env
FLASK_CONF=/home/sanchuang/flask_config.py
>>> import os
>>> print(os.environ)
environ({'FLASK_CONF': '/home/sanchuang/flask_config.py'})

当前
server.py文件

from app import create_app
app = create_app()

app.run(host=app.config["HOST"],
        port=app.config["PORT"],
        debug=app.config["DEBUG"])

app.py文件

import os
from flask import Flask

def create_app(config=None):
    app = Flask(__name__)
    # load default configuration
    app.config.from_object('conf.settings')
    # app.config.from_object('config.secure')

    # load environment configuration
    # FLASK_CONF="/path/to/config_dev.py"
    # FLASK_CONF="/path/to/config_prod.py"
    # 也可以根据系统环境变量,加载不同的配置文件
    if 'FLASK_CONF' in os.environ:
        app.config.from_envvar('FLASK_CONF')

    # load app sepcified configuration
    if config is not None:
        if isinstance(config, dict):
            app.config.update(config)
        elif config.endswith('.py'):
            app.config.from_pyfile(config)
    return app

conf目录 --> setting.py文件

HOST = "0.0.0.0"
PORT = 8000
DEBUG = True

视图文件注册到蓝图

router目录 --> view01.py文件

from flask import Blueprint

view01_bp = Blueprint('view01',__name__,url_prefix='/view01/')

@view01_bp.route("/index", methods=["POST","GET"])
def index():
    return "this is blueprint"

router目录 --> view02.py文件

from flask import Blueprint

view02_bp = Blueprint('view02',__name__,url_prefix='/view02/')

@view02_bp.route("/", methods=["POST","GET"])
def index():
    return "this is view02 blueprint"

整合router的注册

router目录 --> init.py文件

from .view01 import view01_bp
from .view02 import view02_bp

def init_app(app):
    app.register_blueprint(view01_bp)
    app.register_blueprint(view02_bp)

server.py文件

#不建议这样写
from app import create_app
from router import init_app

app = create_app()
init_app(app)

app.run(host=app.config["HOST"],
        port=app.config["PORT"],
        debug=app.config["DEBUG"])

蓝图注册到app

app.py文件

import os
from flask import Flask

def create_app(config=None):
    app = Flask(__name__)
    # load default configuration
    app.config.from_object('conf.settings')
    # app.config.from_object('config.secure')

    # load environment configuration
    # FLASK_CONF="/path/to/config_dev.py"
    # FLASK_CONF="/path/to/config_prod.py"
    # 也可以根据系统环境变量,加载不同的配置文件
    if 'FLASK_CONF' in os.environ:
        app.config.from_envvar('FLASK_CONF')

    # load app sepcified configuration
    if config is not None:
        if isinstance(config, dict):
            app.config.update(config)
        elif config.endswith('.py'):
            app.config.from_pyfile(config)
    #注册蓝图				#注:改的地方
    from router import init_app
    init_app(app)

    return app

主文件 server.py

from app import create_app

app = create_app()

app.run(host=app.config["HOST"],
        port=app.config["PORT"],
        debug=app.config["DEBUG"])
#结果为
127.0.0.1:8000/view01/index		this is blueprint
127.0.0.1:8000/view02/			this is view02 blueprint

api Application Programming Interface 应用程序接口

前后端分离:前后端代码独立
验证登录成功/失败,后端 flask做,前端使用ajax技术发起请求,请求后端flask的接口api,验证返回结果

29_python笔记-flask框架_第7张图片
前后端分离 好处:页面分开,各自写各自的页面,统一调用接口就可以了
前后端隔离 好处:可以并行开发,增加开发效率,增加人力资源的利用

json数据的展示

router目录 view01.py文件下

from flask import Blueprint
import json
view01_bp = Blueprint('view01',__name__,url_prefix='/view01/')

@view01_bp.route("/index", methods=["POST","GET"])
def index():
    dict1 = {
        "name":"wen",
        "age":18,
        "sex":"女"
    }
    return json.dumps(dict1)

29_python笔记-flask框架_第8张图片
获取数据成功
29_python笔记-flask框架_第9张图片

新闻资讯站点示例

静态页面展示
步骤1:创建static、templates目录
29_python笔记-flask框架_第10张图片
步骤2:
将index.html放到 templates下面 (模板文件,要返回的页面内容)
将css、images、js放到static下面 (静态资源)
29_python笔记-flask框架_第11张图片
步骤3:导入render_template模块,进行访问
router目录 view02.py文件下

from flask import Blueprint, render_template

view02_bp = Blueprint('view02',__name__,url_prefix='/view02/')

@view02_bp.route("/index", methods=["POST","GET"])
def index():
    return render_template("index.html")

29_python笔记-flask框架_第12张图片
结果 127.0.0.1:8000/view02/index 访问成功,页面图片加载不出来
29_python笔记-flask框架_第13张图片
29_python笔记-flask框架_第14张图片
步骤:构造url (需要导入url_for模块)

from flask import Blueprint, url_for
print( url_for("static",filename="/view02/images/01.jpg"))
#注:前面static和后面/view02/images/01.jpg 拼接

步骤:
((css|js|images).*?(css|js|jpg|png|gif|bmp))
{{ url_for(‘static’, filename=’$1’) }} #注:2个{}是jinja2的语法
批量匹配和替换 pycharm里面 ctrl+r
然后勾选 match case 和 regex 支持正则
29_python笔记-flask框架_第15张图片
在html里面使用Python语法,使用了jinja2引擎,所以{{ url_for(‘static’, filename=’$1’) }}
有2个括号,$1表示最前面的那个组(最外面的括号)
29_python笔记-flask框架_第16张图片
index.html不是纯粹的网页模板,使用了jinja2模板引擎 渲染

现在静态页面都是写死了的,所以 写到数据库里。现在假设数据放在字典里,如何把HTML和Python结合起来。templates下 把模板和数据做结合,使用jinja2模板引擎

router目录 view02.py文件下

from flask import Blueprint, render_template
view02_bp = Blueprint('view02',__name__,url_prefix='/view02/')
@view02_bp.route("/index", methods=["POST","GET"])
def index():
    content = {
        "title":"标题一",
        "childtitle":"标题二"
    }
    #返回html前将模板和数据做一个结合
    result_html = render_template("index.html", content=content)  #注:接收可变成长关键字参数
    return result_html

29_python笔记-flask框架_第17张图片
因为没学数据库,所以数据先放在字典里

templates目录 index.html文件下
#注:使用jinja2的语法 {{ content.title }} {{ content.childtitle }}
图示
渲染成功
29_python笔记-flask框架_第18张图片
#注:更倾向于 字典解包

router目录 view02.py文件
29_python笔记-flask框架_第19张图片
templates目录 index.html文件
29_python笔记-flask框架_第20张图片
#注:渲染成功
29_python笔记-flask框架_第21张图片

Flask数据模型

Python3 MySQL 数据库连接 执行SQL语句 在数据库里面运行

[root@cPen_aliyun ~]# pip3 install PyMySQL
>>> import pymysql	
>>> conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",passwd="******",database="flask_app")
>>> conn
<pymysql.connections.Connection object at 0x7fdb4ccfdc18>
>>> cursor = conn.cursor()	#注:创建游标,操控数据库的对象,建立在连接上面的
>>> cursor.execute("select version()")	#注:在这个对象上执行语句
1
>>> data = cursor.fetchone()			#注:返回的结果就是fetchone
>>> data
('5.7.31',)
#注:fetchone抓取一行,fetchall抓取全部

MySQL WorkBench里 建表操作

use sanchuang;			#注:进入库
create table stu (			#注:创建表
id int not null primary key,
name varchar(128),
sex varchar(64),
age int);

步骤:stu右键 Send to SQL Editor --> 右边 Insert Statement
#注:向表里插入数据
29_python笔记-flask框架_第22张图片

INSERT INTO `sanchuang`.`stu`
(`id`,
`name`,
`sex`,
`age`)
VALUES
(<{id: }>,
<{name: }>,
<{sex: }>,
<{age: }>);
(1,			#注:向表插入数据
"wy",
"f",
18);	

步骤:stu右键 Select Rows (第一行) 查看表数据
29_python笔记-flask框架_第23张图片

>>> conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",passwd="******",database="sanchuang")
>>> cursor = conn.cursor()
>>> cursor.execute("select * from stu")		#注:从stu表里查询所有的记录
2
>>> data = cursor.fetchone()		#注:fetchone抓取一行,fetchall抓取全部
>>> print(data)
(1, 'wy', 'f', 18)
>>> data = cursor.fetchone()
>>> print(data)
(2, 'wy2', 'f', 18)

#注:MySQL WorkBench里 设置为默认库的操作
Sanchuang 右键 --> Set as Default Schema 设置为默认库 (sanchuang字体会加粗)
29_python笔记-flask框架_第24张图片

ORM

ORM,即Object-Relational Mapping(对象关系映射)
引入ORM,中间层,帮我们去连数据库,我们只需要操纵中间层ORM,不需要我们去关注 数据库的连接和管理。
#步骤:安装模块 (pycharm里执行)

pip install flask-sqlalchemy

#步骤:pycharm里面 创建 model目录
#步骤:model目录下 创建user.py、base.py、init.py模块

在model目录 base.py模块下 创建ORM对象 (创建实例)
#注:考虑到后期models拆分,单独创建ORM对象: models/base.py

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
  
from . import user			#注:把定义好的模型导入	

#步骤:定义数据模型
在model目录 user.py模块下 导入db 定义数据模型

from .base import db

class Stu(db.Model):			#注:继承db的Model类  它已经是有映射关系的类了
    __tablename__ = "stu"		#注:创建的数据模型 stu,映射到数据库里的stu表
    id = db.Column(db.INTEGER,primary_key=True)
    name = db.Column(db.String)
    sex = db.Column(db.String)
    age = db.Column(db.INTEGER)
#注:对象关系映射,操纵这个类就是操纵这个数据库了

在model目录 init.py模块下

from .base import db

def init_app(app):
    db.init_app(app)
    #db.create_all(app=app)   #注:如果数据库里没有那个表就创建

在app.py文件下

#注册初始化数据模型
from model import init_app
init_app(app)

步骤:数据库的连接
#注:可以在实例化之后,修改SQLALCHEMY_DATABASE_URI 属性,或者在实例化之前,继承Flask类时,重写__init__

在conf目录 settings.py文件下

SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:******@47.110.144.55:3306/sanchuang"
app.py 实例化对象时  会读取这个文件里的参数,对属性进行修改

在router目录 view01.py文件下

from flask import Blueprint, url_for
import json
from model.user import Stu				#注:加这一句

view01_bp = Blueprint('view01',__name__,url_prefix='/view01/')

@view01_bp.route("/index", methods=["POST","GET"])
def index():
    user_info = Stu.query.all()			#注:改这里
    return user_info		#注:Stu.query.all()查询语句,查询所有的字段。必须得遵循一定的规范

pycharm里安装 PyMySQL

pip install PyMySQL

步骤:Postman访问127.0.0.1:8000/view01/index
显示TypeError,因为查找的 Stu.query.all() 是对象,不能直接做HTML的返回。
可以为对象转换格式,最好转换成string类型格式去输出
TypeError: The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a list.

可以为对象转换格式,最好转换成string类型格式去输出
方法1转换成字典
在model目录 user.py文件下
#注:重写__init__方法,添加新方法 to_json

from .base import db

class Stu(db.Model):
    __tablename__ = "stu"
    id = db.Column(db.INTEGER,primary_key=True)
    name = db.Column(db.String)
    sex = db.Column(db.String)
    age = db.Column(db.INTEGER)

    def __init__(self,id,name,sex,age):	#注:重写__init__方法
        self.id = id
        self.name = name
        self.sex = sex
        self.age = age

    def to_json(self):		#注:添加 to_json方法
        return {
            "id":self.id,
            "name":self.name,
            "sex":self.sex,
            "age":self.age
        }

在router目录 view01.py文件下

from flask import Blueprint, url_for
import json
from model.user import Stu

view01_bp = Blueprint('view01',__name__,url_prefix='/view01/')

@view01_bp.route("/index", methods=["POST","GET"])
def index():
    user_info = Stu.query.all()	#注:Stu.query.all() 查询Stu里面所有的记录的字段
	#注:对象是保存在内存里的,不能直接返回,HTML不认识
    result_list = []			#注:修改的地方 
    for user in user_info:		#注:循环获取它里面的每一个对象
        result_list.append(user.to_json())  #注:直接添加 user.to_json(),把它转换成字典
	#注:最后得到的是 列表里面包字典 符合  json格式的字符串
    return json.dumps(result_list)	#注:所有返回json格式

步骤:Postman访问
127.0.0.1:8000/view01/index #注:得到的结果就是数据库里的东西
29_python笔记-flask框架_第25张图片

sqlite本地文件数据库(本地测试使用)

flask_app --> conf目录 --> setting.py文件

import os
basedir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
print(os.path.dirname(__file__))
#注:获取上层路径    conf
# E:/web_cpen/flask_proj/flask_app/conf
print(os.path.dirname(os.path.dirname(__file__)))	#注:flask_app的绝对路径
##注:获取上上层路径 flask_app
# E:/web_cpen/flask_proj/flask_app
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
print(os.path.join(basedir, 'data.sqlite'))
#结果 E:\web_cpen\flask_proj\flask_app\data.sqlite
import os
basedir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
#print(os.path.dirname(os.path.dirname(__file__)))

#sqlite的连接方式
#sqlite 本地文件数据库 一般用于测试
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
print(os.path.join(basedir, 'data.sqlite'))

flask_app --> model目录 --> __init__文件

from .base import db

def init_app(app):
    db.init_app(app)
    db.create_all(app=app)		#注:加这一句  自动去数据库里创建表 (这个表和)
	#注:如果是连接数据库,更改表需要考虑 这个用户 有没有权限
	#注:用户如果 有权限 可以这样创建表

flask_app --> server.py文件 运行
#注:生成了data.sqlite文件
#注:将生成的data.sqlite文件拖过去

29_python笔记-flask框架_第26张图片
步骤:点 + 号 --> Data Source --> Sqlite(Xerial)
29_python笔记-flask框架_第27张图片
步骤:点download 下载插件
29_python笔记-flask框架_第28张图片
显示成功
29_python笔记-flask框架_第29张图片
Database 点 + 号 --> Data Source --> 添加MySQL
29_python笔记-flask框架_第30张图片
点 + 号插入数据
29_python笔记-flask框架_第31张图片

数据模型定义方法2

flask_app --> model目录 --> user.py文件

from sqlalchemy import Column, String		#注:加这句话  第二种模型的定义
from .base import db

class Stu(db.Model):
    __tablename__ = "stu"
    id = db.Column(db.INTEGER,primary_key=True)
    name = db.Column(db.String,unique=True)	#注:唯一索引
    sex = db.Column(db.String)
    age = db.Column(db.INTEGER)
    email = Column(String)		#注:加这句话 第二种模型的定义,就不用写db 了

flask_app --> router目录 --> view01.py

from flask import Blueprint, url_for
import json
from model.user import Stu

view01_bp = Blueprint('view01',__name__,url_prefix='/view01/')

@view01_bp.route("/get", methods=["POST","GET"])	#注:添加的部分
def get():					#注:添加的部分
    user_info = Stu.query.all()
    result_list = []
    for user in user_info:
        result_list.append(user.to_json())
    #print( url_for("static",filename="/view02/images/01.jpg"))
    return json.dumps(result_list)

flask_app --> model目录 --> user.py文件下 #注:新增email

from sqlalchemy import Column, String
from .base import db

class Stu(db.Model):
    __tablename__ = "stu"
    id = db.Column(db.INTEGER,primary_key=True)
	…………
    email = Column(String)

    def __init__(self,id,name,sex,age,email):
	…………
        self.email = email

    def to_json(self):
        return {
	…………
            "email":self.email
        }

Flask命令行

创建文件:跟server.py同级的 manage.py文件 flask_app --> manage.py文件

Terminal命令行 安装:pip install flask_script

Flask的Web开发服务器支持很多启动设置选项,但只能在脚本中作为参数传给app.run()函数。这种方 式很不方便,传递设置选项的理想方式是使用命令行参数

flask_app --> manage.py文件
#注:输入如下 使用flask命令行(manage类) 去管理app

#flask命令行
from flask_script import Manager
from app import create_app
app = create_app()

manager = Manager(app)
if __name__ == '__main__':
    manager.run()

为了让Flask的启动更加灵活,可以使用Flask-Script中自带的runserver
#注:命令行还可以操控数据库,不推荐以前使用的 flask_app --> app.py 文件下的 create_app() 操作

步骤:终端启动 在gitbash里面运行 python manage.py runserver -d -h 0.0.0.0 -p 5000

#注:-d 进入debug模式 -h hostname -p 端口
#注:先进入虚拟环境,再执行命令
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj
$ source venv/Scripts/activate			#注:source加载环境变量  进入虚拟环境 
(venv)
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj
$ cd flask_app/
(venv)
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ ls
__pycache__/  conf/        manage.py  router/    static/
app.py        data.sqlite  model/     server.py  templates/
(venv)
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ python manage.py runserver -d -h 0.0.0.0 -p 8000		#注:执行此命令
#注:runserver 运行服务
 * Serving Flask app "app" (lazy loading)
 * Environment: production
…………
 * Debugger is active!
 * Debugger PIN: 637-344-130
 * Running on http://0.0.0.0:8000/ (Press CTRL+C to quit)


#注:命令行还可以操控数据库,不推荐以前的create_app操作

Terminal 命令行 安装 flask_migrate

(venv) E:\web_cpen\flask_proj\flask_app>pip install flask_migrate

flask_app --> manage.py文件 导入
步骤:数据库迁移: manage.py

from flask_migrate import Migrate, MigrateCommand
from model import db		
#注:因为db写在了__init__.py里,所有可以直接导入
#注:如果写在 model --> user.py  则from model.user import *
#flask命令行
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand		#注:添加的地方
from model import db					#注:添加的地方
from app import create_app
app = create_app()

manager = Manager(app)

# 创建db管理工具 => app, db			#注:添加这几句
# 注意,如果是sqlite数据库需要修改
migrate = Migrate(app, db)

# 添加迁移脚本的命令到manager中
manager.add_command('db', MigrateCommand)	#注:添加命令  添加的命令

if __name__ == '__main__':
    manager.run()

Gitbash操作

#注:在 E:\web_cpen\flask_proj\flask_app 右键 Git bash here (又一个gitbash界面)

15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ source ../venv/Scripts/activate		#注:进入虚拟环境
(venv)
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ ls
__pycache__/  conf/        manage.py  router/    static/
app.py        data.sqlite  model/     server.py  templates/
(venv)
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ python manage.py db init			#注:初始化操作
…………
'E:\\web_cpen\\flask_proj\\flask_app\\migrations\\alembic.ini' before proceeding.
(venv)
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$

初始化后pycharm界面 生成 migrations目录 记录mysql的版本。可以删除,每次init初始化后会自动生成
29_python笔记-flask框架_第32张图片
在gitbash里面操作

#注:执行 python manage.py db migrate -m "01"
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ python manage.py db migrate -m "01"		#注:-m “01”是执行的注释

产生文件: migrations目录 --> version -->63477……文件
#注:对数据库的操作都在这个文件里 #注:migrate 迁移
29_python笔记-flask框架_第33张图片

15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ python manage.py db upgrade

出错了

步骤:
1、删除 migrations目录
2、在terminal命令行 重新执行3步

(venv) E:\web_cpen\flask_proj\flask_app>python manage.py db init
(venv) E:\web_cpen\flask_proj\flask_app>python manage.py db migrate -m "01"
(venv) E:\web_cpen\flask_proj\flask_app>python manage.py db upgrade

#结果:一样的错误,因为以前创的表 长度 和现在初始化的长度不一样
#注:string类型映射在里面是varchar类型,所以需要指定长度
#报错显示:… sqlalchemy.exc.CompileError: VARCHAR requires a length on dialect mysql
#注:所以string类型需要指定长度 或者 删除mysql里的表

string类型需要指定长度
flask_app --> model目录 --> user.py文件下

class Stu(db.Model):
    __tablename__ = "stu"
    id = db.Column(db.INTEGER,primary_key=True)
    name = db.Column(db.String(64),unique=True)		#注:添加数字
    sex = db.Column(db.String(64))			#注:添加数字
    age = db.Column(db.INTEGER)
    email = Column(String(128))			#注:添加数字

#注:本地 migration --> versions 记录它的 版本号,同时在数据库里创建一个记录版本号的表。每一次做变更 对比版本号。版本号有偏差 ,提交不上来。把alembic_version表和migrations都删了,再重新init 重新提交
29_python笔记-flask框架_第34张图片
29_python笔记-flask框架_第35张图片
#注:version_num 只显示 最近一次的版本

模型 新增一列 add_time

import datetime						#注:加这句话
from sqlalchemy import Column, String, DateTime			#注:加这句话
from .base import db

class Stu(db.Model):
    __tablename__ = "stu"
    id = db.Column(db.INTEGER,primary_key=True)
    name = db.Column(db.String(64),unique=True)
    sex = db.Column(db.String(64))
    age = db.Column(db.INTEGER)
    email = Column(String(128))
    add_time = Column(DateTime, default=datetime.datetime.now())	#注:加这一句话 (上传的话 不传add_time 自动添加datetime)

#注:不需要 init 初始化版本了,以及有版本了,重复后面2步操作

(venv) E:\web_cpen\flask_proj\flask_app>python manage.py db migrate -m "02"
(venv) E:\web_cpen\flask_proj\flask_app>python manage.py db upgrade

#注:这样 表里面就有了 add_time
#注:你有这个写的权限 就可以这么做了。 在线上 编写程序可以,但工作时不可以 需要向DBA申请权限

回滚版本 使用语句: python manage.py db downgrade (upgrade) 版本号

使用MySQL Workbench 插入语句

#步骤:点击左上角 +SQL 新增一个SQL页

desc stu;					#注:查看表结构

29_python笔记-flask框架_第36张图片
#注:数据库里给的 add_time default 是NULL,默认给NULL值。除主键外 每个字段 Null 都可以为空(YES)。auto_increment 自增。

insert into stu(id,name,sex,age) values(3,"wy3","f",19)	#注:插入数据

#注:左边的 id,name,sex,age 表示要插入的 字段。只对这四个字段插入数据

select * from stu	#注:查看表数据

29_python笔记-flask框架_第37张图片
#注:只插入了4个数据

insert into stu(name,sex,age) values("wy2","f",19)		#注:id为2  因为会自增auto_increment

#注:id不插入的情况:成功了,id 会自增 (auto increment),id不为空 (NULL NO)
29_python笔记-flask框架_第38张图片
#注:id自增1

#注:add_time 在python给的 default值 ,是中间ORM对象关系映射 中间那层的(python的),不是数据库的
这个datetime是Python的 ORM 的值,不是数据库的。直接直接操控数据库,数据库没有default 这个值

数据库增删查改

requests属性
args 接收请求的url参数 dict
form 记录请求中的表单数据 dict
headers 记录请求中的报文头
method 记录请求中http的使用方法 string
url 记录请求的url地址

查询

(venv) E:\web_cpen\flask_proj\flask_app>python manage.py shell
#注:进入当前flask上下文环境
>>> from model.user import Stu		#注:把model导入进来,使用这个模型
>>> user_info = Stu.query.all()		
#注:all查询出来的是列表对象,每一项 就是这个表的数据
>>> user_info
[<Stu 1>, <Stu 2>]
>>> for i in user_info:
...     print(i)
...
<Stu 1>						#注:每个对象都是数据库 表的实例 必须转换成json格式
<Stu 2>
#注:Stu.query.all()生成类似列表一样的东西,列表里面的 ,每一个对象 就是 每一行数据 (刚才新增的2行数据)

29_python笔记-flask框架_第39张图片
#注:在pycharm里 可以这样 添加数据库 数据
29_python笔记-flask框架_第40张图片
#注:这时 Stu.query.all() 列表里面就有3个了,每一行数据 都是一个数据库对象
#注:捞取这个 数据库的内容 不能直接返回。因为返回的是网页内容,数据库对象 网页识别不了,http协议不支持 展示不了。所以把它转化为 json格式
29_python笔记-flask框架_第41张图片
request 获取请求里的信息

#注:捞取这个 数据库的内容 不能直接返回。因为返回的是网页内容,数据库对象 网页识别不了,http协议不支持 展示不了。所以把它转化为 json格式。for 循环取的是它的每一个对象,to_json方法 把它 转换成 字典,加入到列表里面。列表里面包字典 符合json格式,使用json.dumps() 把它转化成json格式的字符串 ,再返回过去

#注:如果只想 获取(get) 某一个 (比如只想get id为1的详细信息),这个请求 应该由客户端告诉我,可以在url 后面传递 参数 (?号之后 都是它传递的参数,可以传递多个参数,用&号分割)

flask_app --> router目录下 --> view01.py文件里
#注:request.args 获取的参数 是一个字典 {“id”:”1”}
29_python笔记-flask框架_第42张图片
#注:导入flask 里面 request对象 ,获取url 里面的参数( request 获取请求里的信息)

from flask import Blueprint, url_for,request		#注:改的地方
import json
from model.user import Stu

view01_bp = Blueprint('view01',__name__,url_prefix='/')	#注:‘/’改的地方

@view01_bp.route("/get", methods=["POST","GET"])		
def get():						#注:改的地方,逻辑
    id = request.args.get("id","all")			#注:这样获取 传递进来的id
    result_list = []					#注:使用get 万一没有传递id,给all值
					#注:列表放到外面  是为了兼容
    if id == 'all':					#注:如果传递进来的id=all时
        user_info = Stu.query.all()			#注:all  查询所有 数据库记录的对象
        for user in user_info:		#注:Stu.query.all()生成类似列表一样的东西
            result_list.append(user.to_json())	#注:这里 将表里的实例转换成json格式 才能识别
    else:
        user_info = Stu.query.get(int(id))	#注:Stu.query.get 获取 id的数据,把id转换成int类型
				#注:使用get去 获取(查询) 它的主键 
        result_list.append(user_info.to_json())
    return json.dumps(result_list)
127.0.0.1:8000/get
127.0.0.1:8000/get?id=2

get
29_python笔记-flask框架_第43张图片
或者:在路由那里传递参数 <>括起来

flask_app --> router目录下 --> view01.py文件里

from flask import Blueprint, url_for,request
import json
from model.user import Stu

view01_bp = Blueprint('view01',__name__,url_prefix='/')

@view01_bp.route("/get/", methods=["POST","GET"])	#注:这里
def get(id):					#注:这里
    #id = request.args.get("id","all")
    result_list = []
    if id == 'all':
        user_info = Stu.query.all()
        for user in user_info:
            result_list.append(user.to_json())
    else:
        user_info = Stu.query.get(int(id))
        result_list.append(user_info.to_json())
    return json.dumps(result_list)

Postman里面
127.0.0.1:8000/get/1
127.0.0.1:8000/get/all

29_python笔记-flask框架_第44张图片
#注:按条件查询 (过滤查询 Stu.query.filter_by(name=‘wy’).all())

#注:在terminal命令行输入

>>> stu1 = Stu.query.filter_by(name='wy').all()
>>> stu1
[<Stu 1>]
>>> stu1 = Stu.query.filter_by(age=19).all()
>>> stu1
[<Stu 1>, <Stu 2>]
>>> stu1 = Stu.query.filter_by(age=19).first()
>>> stu1
<Stu 1>
#注:all 查询所有,first查询第一个。all返回列表,first返回对象
>>> stu1 = Stu.query.filter(Stu.age<20).all()  #注:查找年龄小于20的
>>> stu1
[<Stu 1>, <Stu 2>]


select * from stu limit 1            #注:限制查询1条数据
select * from stu limit 1 offset 1     #注:offset 偏移  从第2个查

增加

选择 Body --> form-data 表单型数据 POST方法:提交方法
29_python笔记-flask框架_第45张图片
#注:表单型数据做提交

flask_app --> router目录 --> view01.py文件

#注:增加的部分
@view01_bp.route("/add", methods=["POST", "GET"])
def add():
    data = request.form			#注:核心点
    return json.dumps(data)

postman下
29_python笔记-flask框架_第46张图片
完善

@view01_bp.route("/add", methods=["POST", "GET"])
def add():
    data = request.form
    user = Stu(data["id"],  #注:实例化对象,生成一个数据库对象
               data["name"],
               data["sex"],
               data["age"],
               data["email"])
    db.session.add(user)	#注:传的参数就是 生成的数据库对象
    db.session.commit()     #注:提交
    
    return json.dumps(data)

29_python笔记-flask框架_第47张图片
提交成功 状态码200
29_python笔记-flask框架_第48张图片
#注:添加数据成功
#注:不想传 id 让它自增,那么 postman 里面把id 删掉

修改

#注:修改id为4的 数据

flask_app --> router目录 --> view01.py文件

@view01_bp.route("/modify", methods=["POST", "PUT"])	#注:修改使用PUT  改的地方
def modify():
    id = request.args.get("id",1, type=int) #注:没有就给1   指定整型
    data = request.form #注:从form表单获取它的详细信息

    user = Stu.query.get(id)    #注:查询跟id一样的user信息
    user.name = data["name"]    #注:对对象属性的操作 修改属性值
    user.sex = data["sex"]
    user.age = data["age"]
    user.email = data["email"]  #注:对数据库记录的属性进行修改,还没提交到数据库

    db.session.add(user)
    db.session.commit()
    return "modify user id ok"

29_python笔记-flask框架_第49张图片
29_python笔记-flask框架_第50张图片

删除

flask_app --> router目录 --> view01.py文件

@view01_bp.route("/delete/", methods=["DELETE"])
#注:可以先 指定类型 int  id
def delete(id):
    user = Stu.query.get(id)    #注:先查询到这个id的相关数据
    if user:
        db.session.delete(user)
        db.session.commit()
    return "delete ok"

Postman下:127.0.0.1:8000/delete/3
29_python笔记-flask框架_第51张图片
数据删除成功

api 统一标准返回

api做统一状态返回
做标准返回,返回的数据格式要一致

步骤:创建libs目录 --> uilts.py文件
放公共里面的内容(公共函数、公共类等)
utils一般放工具的
api开发统一规范

flask_app --> libs目录 --> uilts.py文件

class UTIL:
    @staticmethod
    def to_json(status, message, data = None):
        return {"status":status,			#注:状态码
                "message":message,			#注:提示信息
                "data":data				#注:数据
                }

flask_app --> router目录 --> view01.py文件

from libs.uilts import UTIL

view01_bp = Blueprint('view01',__name__,url_prefix='/')

@view01_bp.route("/get/", methods=["POST","GET"])
def get(id):				#注:改的地方如下
    #
    try:
        result_list = []
        if id == 'all':
            user_info = Stu.query.all()
            for user in user_info:
                result_list.append(user.to_json())
        else:
            user_info = Stu.query.get(int(id))
            result_list.append(user_info.to_json())
        return UTIL.to_json(status=0, message="get data is ok", data=result_list)
    except Exception as e:
        return UTIL.to_json(status=1, message=e)	#注:改的地方 UTIL.to_json()
#注:UTIL.to_json()  是定义的标准返回

Postman下
29_python笔记-flask框架_第52张图片
29_python笔记-flask框架_第53张图片
请求成功 status为0表示请求成功

[root@cPen_aliyun ~]# systemctl list-unit-files	#注:查看程序是否开机自启

启动

15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj
$ source venv/Scripts/activate							#注:先进入虚拟环境
(venv)
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj
$ cd flask_app/
(venv)
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ ls
__pycache__/  conf/        libs/      migrations/  router/    static/
app.py        data.sqlite  manage.py  model/       server.py  templates/
(venv)
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj/flask_app (master)
$ python manage.py runserver -d -h 0.0.0.0 -p 8000		#注:启动
…………
 * Running on http://0.0.0.0:8000/ (Press CTRL+C to quit)
(venv) E:\web_cpen\flask_proj\flask_app>python manage.py db init
(venv) E:\web_cpen\flask_proj\flask_app>python manage.py db migrate -m "01"
(venv) E:\web_cpen\flask_proj\flask_app>python manage.py db upgrade
python manage.py db downgrade (upgrade) 版本号

flask_marshmallow模块 序列化

flask里面 flask_marshmallow 帮你做序列化
Pycharm生成 serializer目录 存放序列化相关的东西

步骤:安装模块 flask-marshmallow

(venv) E:\web_cpen\flask_proj\flask_app>pip install flask-marshmallow

flask_app --> serializer目录 --> init.py文件、base.py文件、user.py文件

flask_app --> serializer目录 --> base.py文件

from flask_marshmallow import Marshmallow
ma = Marshmallow()  #注:生成marshmallow对象

from . import user   #注:把user导入

flask_app --> serializer目录 --> init.py文件

from .base import ma			#导入它

def init_app(app):
    ma.init_app(app)

flask_app --> app.py文件下
#注:在app.py下注册

#序列化
    from serializer import init_app
    init_app(app)

#注:不要把 init_app()都写在一起,只会执行最后一个
#注:因为做了拆分,所以这样写

flask_app --> serializer目录 --> user.py文件

#注:输入以下数据
from .base import ma        #注:导入ma
from model.user import Stu  #注:导入需要序列化的模型

class StuSchema(ma.Schema): #注:继承ma的Schema类
    class Meta:
        model = Stu
        fileds = ("id","name","email")  #注:序列化这三个

#对于单个orm对象
stu_schema = StuSchema()

#对于多个orm对象
stu_schemas = StuSchema(many = True)    #注:使用对象进行序列化

flask_app --> router目录 --> view01.py文件

from serializer.user import stu_schema, stu_schemas    #注:添加这里

@view01_bp.route("/get/", methods=["POST","GET"])
def get(id):
    #
    try:
        result_list = []
        if id == 'all':
            user_info = Stu.query.all()
            user_list = stu_schemas.dump(user_info)    #注:添加这里
            # for user in user_info:
            #     result_list.append(user.to_json())
        else:
            user_info = Stu.query.get(int(id))
            user_list = stu_schema.dump(user_info)    #注:添加这里
            # result_list.append(user_info.to_json())
        return UTIL.to_json(status=0, message="get data is ok", data=user_list) #注:添加这里
    except Exception as e:
        return UTIL.to_json(status=1, message=e)

#结果:postman访问成功
29_python笔记-flask框架_第54张图片
29_python笔记-flask框架_第55张图片
#注:目前写的后端 api接口 (前后端分离)

mvc、mtv模式、前后端分离

######mvc
#web 开发设计的一种模式
#m --> model 数据
#v --> view 视图
#c --> controller 业务逻辑

#注:mvc模式
29_python笔记-flask框架_第56张图片
#mtv模式 python web开发设计模式
#m --> model 数据
#v --> view 视图
#t --> template 模板
29_python笔记-flask框架_第57张图片
#注:t不做任何逻辑处理,数据的处理都在v这里

RestFul API 表现层状态转化

######增删改查
#增加     /student/add
#修改     /student/modify
#删除     /student/delete
#查询     /student/get

#如果再添加一个学校的话
#   /school/add
#   /school/delete

######===> rest api
#表现层(资源)状态转移
#/student
#method   get,  post,  put,  delete

CURD
创建(Create)、更新(Update)、读取(Retrieve)和删除(Delete)

步骤:安装模块 flask-restful

(venv) E:\web_cpen\flask_proj\flask_app>pip install flask-restful

flask_app --> router目录 --> 新建 satuapi.py文件
flask_app --> router目录 --> satuapi.py文件下

#注:输入如下
from flask import Blueprint, request    #注:导入蓝图
from flask_restful import Resource, Api, reqparse
from model.user import Stu
from libs.uilts import UTIL 		#注:导入统一标准返回
from serializer.user import stu_schema, stu_schemas
from model import db

stuapi_bp01 = Blueprint("stuapi",__name__, url_prefix="/api")   #注:注册蓝图

#rest api绑定到蓝图上
api = Api(stuapi_bp01)

class StuView(Resource):    #注:处理Stu资源的视图,继承Resource
def get(self,id):  #注:名字不能改,接收get请求 自动到这个函数	#注:这里
        try:
            if id == "all":
                user_info = Stu.query.all()
                user_list = stu_schemas.dump(user_info)
                # for user in user_info:
                #     result_list.append(user.to_json())
            else:
                user_info = Stu.query.get(int(id))
                user_list = stu_schema.dump(user_info)
                # result_list.append(user_info.to_json())
            return UTIL.to_json(status=0, message="get data is ok", data=user_list)
        except Exception as e:
            return UTIL.to_json(status=1, message=str(e))

    def post(self):						#注:这里
        data = request.form
        user = Stu(data["id"],
                   data["name"],
                   data["sex"],
                   data["age"],
                   data["email"])
        db.session.add(user)
        db.session.commit()
        return UTIL.to_json(status=0, message="add data is ok")

#设置路由
api.add_resource(StuView,"/student/")		
api.add_resource(StuView,"/student/", endpoint = "studentapi")	#注:取一个不一样的endpoint

#注:设计2种url  无论访问哪种url 都会交给StuView去处理。如果是get方法,就跳到get函数去处理,所以函数名不能变(状态转换)

flask_app --> router目录 --> init.py文件下

from .stuapi import stuapi_bp01		#注:添加这句

def init_app(app):
    app.register_blueprint(view01_bp)
    app.register_blueprint(view02_bp)
    app.register_blueprint(stuapi_bp01)		#注:添加这句,注册蓝图

Postman访问结果
GET查询 127.0.0.1:8000/api/student/1
GET查询 127.0.0.1:8000/api/student/all
29_python笔记-flask框架_第58张图片
29_python笔记-flask框架_第59张图片
POST添加 127.0.0.1:8000/api/student/ 设置id name sex age email
29_python笔记-flask框架_第60张图片

自带的序列化工具

flask_app --> router目录 --> satuapi.py文件下

from flask_restful import marshal_with, fields

#定义返回的格式
stu_resource_fields = {			#注:定义返回的格式
    "status":fields.String,
    "message":fields.String,
    "data":fields.List(fields.Nested({
        "name":fields.String,
        "age":fields.Integer,
        "email":fields.String
    }))
}

class StuView(Resource):
    @marshal_with(stu_resource_fields)		#注:交给装饰器输出
    def get(self,id):  
        try:
            if id == "all":
                user_info = Stu.query.all()
                #user_list = stu_schemas.dump(user_info)
                # for user in user_info:
                #     result_list.append(user.to_json())
            else:
                user_info = Stu.query.get(int(id))
                #user_list = stu_schema.dump(user_info)
                # result_list.append(user_info.to_json())
            return UTIL.to_json(status=0, message="get data is ok", data=user_info)
        except Exception as e:
            return UTIL.to_json(status=1, message=str(e))

Postman下
GET 127.0.0.1:8000/api/student/all
GET 127.0.0.1:8000/api/student/1
查询成功

参数解析

flask_app --> router目录 --> satuapi.py文件下

from flask_restful import Resource, Api, reqparse

class StuView(Resource):

    def __init__(self):		#注:添加的地方
        #创建一个解析器对象
        self.parse = reqparse.RequestParser()   #注:指定参数解析对象
        #利用解析器,添加需要验证的参数
        self.parse.add_argument("id",help="学生id错误",type=int) #注:help错误提示
        self.parse.add_argument("name",help="学生name必填",required = True)
        self.parse.add_argument("sex",help="学生性别必填",required = True)
        self.parse.add_argument("age",help="学生age必填",required = True)
        self.parse.add_argument("email",help="学生email必填",required = True)

def post(self):
    #data = request.form
    data = self.parse.parse_args()  	#注:改的地方
    user = Stu(data["id"],	#注:从解析器里面捞取信息,而不是和以前一样从form表单获取
               data["name"],
               data["sex"],
               data["age"],
               data["email"])
    db.session.add(user)
    db.session.commit()
    return UTIL.to_json(status=0, message="add data is ok")

#注:是解析器给我们的返回,data是解析器帮我们验证的。
#注:中间商 解析器  帮我们解析参数 是否正确,如果ok就返回,不ok就报错

Postman下
POST 127.0.0.1:8000/api/student/
29_python笔记-flask框架_第61张图片
29_python笔记-flask框架_第62张图片

CMDB

资产管理
应用管理
指令下发
监控系统
网络拓扑

https://admin.iviewui.com/home
https://wenku.baidu.com/view/584b847559eef8c75fbfb397

https://gitee.com/openspug/spug
https://gitee.com/opendevops/opendevops

地址:https://demo.opendevops.cn/login
用户:demo
密码:2ZbFYNv9WibWcR7GB6kcEY

29_python笔记-flask框架_第63张图片
29_python笔记-flask框架_第64张图片

[root@cPen_B ~]# ls		#注:中控器  B机器
cmdbclient2020-linux.zip	#注:服务器信息收集的脚本
[root@cPen_B ~]# yum install unzip
[root@cPen_B ~]# unzip cmdbclient2020-linux.zip 	#注:解压脚本

[root@cPen_B ~]# dmidecode			#注:看硬件相关信息
[root@cPen_B ~]# dmidecode t men		#注:看内存信息

先部署中控机

[root@cPen_B .ssh]# pip3 install paramiko			#注:安装paramiko库
[root@cPen_B ~]# cd cmdbclient2020-linux
[root@cPen_B cmdbclient2020-linux]# vim servers_info.py 
def get_servers():
    """
    从cmdb-api获取要采集哪些机器的数据数据
    :return:
    """
    # 从接口获取数据
    server_list = {					#注:改的地方
        "sn1":"192.168.0.65",
        "sn2":"192.168.0.36"
    }
    return server_list

def copy_file(host,path):
    import os
    os.system("scp -rq -P 2233 cmdbclient2020 {host}:{path} ".format(host=host, path=path))
#注:改的地方  指定2233端口

def ssh2(ip,sn,run_cmd,env_cmd):
…………
        ssh.connect(ip,2233,timeout=5)		#注:改的地方,指定端口
…………
        print('%s\tError\n'%(ip))
…………
[root@cPen_B cmdbclient2020-linux]# python3 servers_info.py 
Begin......
{'sn1': {'status': True, 'message': '', …………'netmask': '255.255.255.0'}}}}}}
End......

打印捕获信息

try:
……
except Exception as e:
        print(str(e))

CMDB数据模型建立

15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj
$ mkdir devopscmdb
15334@LAPTOP-5GGR0QTF MINGW64 /e/web_cpen/flask_proj
$ cp -r flask_app/* devopscmdb/		#注:复制原来的项目配置 到这个项目
#注:删掉不需要的配置
#注:项目的大体框架

29_python笔记-flask框架_第65张图片

一对多关系

数据模型定义
资产与厂商(一对多关系)

devopscmdb --> model文件 --> cmdb.server.py文件下

#注:输入如下全部代码
#注:定义数据模型
from .base import db		#注:导入db
from libs.enums import AssertType	#注:导入枚举类

class Asset(db.Model):  #注:定义资产信息表,继承db.Model类
    __tablename__ = "asset"	#注:定义表名

    #定义资产id  唯一标识,设为主键,设置自增
    asset_id = db.Column(db.INTEGER, primary_key=True, autoincrement=True)#注:定义id 自增主键
    #asset_type = db.Column(db.Enum("networ","server"))
    #定义资产类型,枚举类
    #1--》server  2--》network
    asset_type = db.Column(db.Enum(AssertType))
    asset_hostname = db.Column(db.String(64))	#注:VARCHAR类型  64位
    asset_sn = db.Column(db.String(128), nullable=False, unique=True)
    #设置外键 约束				                  #注:厂商id 引入外键 ForeignKey
    #设置当前表manufactory字段,外键到manufactory表的manufactory_id字段
    #db.ForeignKey("表名.字段名")
    manufactory_id = db.Column(db.ForeignKey('manufactory.manufactory_id'))

class Manufactory(db.Model):
    """厂商表"""
    __tablename__= "manufactory"
    manufactory_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    manufactory_name = db.Column(db.String(64)) 
    manufactory_tel = db.Column(db.String(11))
######反向查询字段		#注:设置反向查询字段 连接到Asset模型
    assets = db.relationship('Asset', backref="manufactory")	
    #注:backref 给Asset表建立一个manufactory字段

devopscmdb --> libs文件 --> enums.py文件下

#注:输入如下全部代码
#注:libs文件 放一些自定义的东西
import enum #注:导入自带的枚举类

class AssertType(enum.Enum):
    SERVER = 1
    NETWORK = 2

devopscmdb --> model文件 --> base.py文件下

from . import cmdb_server

#注:__init__.py 导入时会运行
from .base import db		又会执行.base
from . import cmdb_server	就会运行。方法1.作为模块导入运行,方法2.作为主程序运行

Pycharm terminal 命令行

(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py db migrate -m "add cmdbserver"
(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py db upgrade

29_python笔记-flask框架_第66张图片
#注:表生成 成功
29_python笔记-flask框架_第67张图片
MySQL Workbench下

#注:查看创建表的语句
show create table asset;

29_python笔记-flask框架_第68张图片
#注:创建的manufactory表仅有3个字段,反向查询字段 在ORM层面
mysql workbench
Pycharm --> terminal命令行

(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py shell	#注:进入当前flask交互环境
>>> from model.cmdb_server import Asset, Manufactory		#注:导入2个表模型
>>> asset_sn1 = Asset(asset_sn="sn1")			#注:添加2个对象
>>> asset_sn2 = Asset(asset_sn="sn2")			#注:给对象一个序列号,其他可以不添加
>>> from model.base import db
>>> db.session.add_all([asset_sn1,asset_sn2])	#注:加入暂存区	可以这种形式 写
>>> db.session.commit()					#注:提交
#注:表有2条记录,没有指定的字段 为空。asset_id 自增  1、2

29_python笔记-flask框架_第69张图片

>>> m1 = Manufactory(manufactory_name="Hp")		#注:Manufactory表插入2个数据
>>> m2 = Manufactory(manufactory_name="DELL")	#注:新建厂商 (厂商模型的对象)
>>> db.session.add(m1)
>>> db.session.add(m2)
>>> db.session.commit()						#注:提交

>>> sn3 = Asset(asset_sn="sn3")
>>> sn4 = Asset(asset_sn="sn4")
>>> db.session.add(sn3)
>>> db.session.add(sn4)
>>> db.session.commit()

>>> m1 = Manufactory.query.filter_by(manufactory_name="Hp").first()	#注:获取对象记录
>>> m2 = Manufactory.query.filter_by(manufactory_name="DELL").first()
>>> sn1 = Asset.query.filter_by(asset_sn="sn1").first()
>>> sn2 = Asset.query.filter_by(asset_sn="sn2").first()
>>> sn3 = Asset.query.filter_by(asset_sn="sn3").first()
>>> sn4 = Asset.query.filter_by(asset_sn="sn4").first()
>>> m1
<Manufactory 1>
>>> sn1
<Asset 1>
>>> dir(sn1)					#注:资产 属性,manufactory 是 厂商表 反向查询字段 给的
[……'manufactory',……]			
#注:厂商表创建assets字段,关联到Asset表,并且为Asset表创建manufactory字段,关联到assets
>>> sn1.manufactory=m1			#注:使用反向查询字段,把对象赋给manufactory (资产属性 有manufactory)
#注:为sn1关联到厂商m1,指定的是sn1.manufactory_id  与下面那种效果一样
>>> sn2.manufactory_id = m2.manufactory_id
>>> db.session.add(sn1)
>>> db.session.add(sn2)
>>> db.session.commit()

>>> sn3.manufactory_id = 1
>>> db.session.add(sn3)
>>> db.session.add(sn4)
>>> db.session.commit()

>>> db.session.rollback()	#注:回滚
>>> m1.assets			#注:惠普厂商的资产	厂商与资产 一对多的关系
[<Asset 1>, <Asset 3>]
>>> m1.assets[0].asset_sn	#注:获取第一个资产的 sn号
'sn1'

>>> sn1.manufactory
<Manufactory 1>
>>> sn1.manufactory.manufactory_name	#注:sn1.manufactory是厂商对象,通过sn1资产 访问 厂商名
'Hp'
>>> dir(m1)			#注:m1有assets 属性 (反向查询字段),数据库不显示,属于ORM层
[……'assets'……]
#注:厂商和资产  一对多的关系

#注:外键非常消耗性能。表创建好之后  可以把数据库里的外键删掉,不会影响 flask层面

devopscmdb --> model文件 --> cmdb.server.py文件下

#注:class Manufactory(db.Model): 添加如下 进行完善和补充
class Manufactory(db.Model):
    ……
# 后期做优化
    note = db.Column(db.Text)
    create_at = db.Column(db.DateTime())
    update_at = db.Column(db.DateTime())
    # 是否启用当前数据
    status = db.Column(db.Integer())	#注:不需要删除数据 设置status就可以

资产与IDC
devopscmdb --> model文件 --> cmdb.server.py文件下

class Asset(db.Model):
    idc_id = db.Column(db.ForeignKey('idc.idc_id'))

class IDC(db.Model):
    __tablename__ = "idc"
    idc_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    # 反向查询: backref='idc'  => Asset.idc
    assets = db.relationship('Asset', backref='idc')
    idc_name = db.Column(db.String(64))
    idc_name_cn = db.Column(db.String(64))
    idc_region = db.Column(db.String(64))
    # 运营商
    idc_isp = db.Column(db.String(64))
    note = db.Column(db.Text)
    create_at = db.Column(db.DateTime())
    update_at = db.Column(db.DateTime())
    status = db.Column(db.Integer())

Terminal命令行下

(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py db migrate -m "add idc"
(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py db upgrade

#注:数据库里的外键删掉,在Python里建立这种外键关系

多对多关系

业务线与用户 (M2M关系)

业务线  理解成应用
业务线与负责人的关系:
一个人负责多个业务线,一个业务线有多个负责人 (多对多的关系)

#解决方法:引入中间表,存它们的映射关系
员工表 、应用表 和 中间表。最终通过id联系在一起
#注:中间表 存放映射关系信息
#注:中间表只存放id,通过id去寻找

devopscmdb --> model文件 --> cmdb.server.py文件下
#注:存放于cmdb有关的数据模型

#注:复制粘贴

from .user import UserProfile			#注:导入
#注:中间表,可以这么写
business_unit_users = db.Table("business_unit_users",
                               # 用户id
                               db.Column("user_profile_id", db.ForeignKey("user_profile.user_profile_id")),
                               # 业务线id
                               db.Column("business_unit_id", db.ForeignKey("business_unit.business_unit_id"))
                               )


class BusinessUnit(db.Model):
    __tablename__ = "business_unit"
    business_unit_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    business_unit_name = db.Column(db.String(64))
    business_unit_name_cn = db.Column(db.String(64))
    # m2m =>好多人 => 管理员
    # # 一个业务应该由很多人来维护, 一个人可以维护多个业务线
    managers = db.relationship("UserProfile",
                               # 指定中间表	
#注:中间表从business_unit_users去捞取,为创建字段business_units
                               secondary=business_unit_users,
                               backref="business_units")
    note = db.Column(db.Text)
    create_at = db.Column(db.DateTime())
    update_at = db.Column(db.DateTime())
    status = db.Column(db.Integer())

devopscmdb --> model文件 --> user.py文件下
#注:存放于user有关的数据模型

#注:复制粘贴

class UserProfile(db.Model):
    __tablename__ = "user_profile"
    user_profile_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    user_profile_name = db.Column(db.String(32), nullable=False)
    user_profile_email = db.Column(db.String(32), nullable=False, unique=True)
    user_profile_mobile =  db.Column(db.String(11))
    note = db.Column(db.Text)
    create_at = db.Column(db.DateTime())
    update_at = db.Column(db.DateTime())
    status = db.Column(db.Integer())

Pycharm terminal 命令行下

#注:生效表
(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py db migrate -m "add idc"
(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py db upgrade		#注:生效

Pycharm terminal 命令行下

>>> from model.cmdb_server import BusinessUnit
>>> from model.user import UserProfile
>>> user1 = UserProfile(user_profile_name="tlf",user_profile_email="[email protected]")	#注:创建3个数据
>>> user2 = UserProfile(user_profile_name="pyf",user_profile_email="[email protected]")
>>> user3 = UserProfile(user_profile_name="xy",user_profile_email="[email protected]")
>>> from model.base import db
>>> db.session.add_all([user1,user2,user3])
>>> db.session.commit()												#注:提交
#注:设置它的业务线
>>> b1 = BusinessUnit(business_unit_name = "cq")
>>> b2 = BusinessUnit(business_unit_name = "mhxy")
>>> b3 = BusinessUnit(business_unit_name = "csgo")
#注:对他进行生效
>>> db.session.add_all([b1,b2,b3])
>>> db.session.commit()
>>> dir(b2)			#注:b2的managers 属性,知道业务线  由谁管
[……'managers'……]
>>> b2.managers		#注:b2目前没有负责人
[]
>>> b2.business_unit_name		#注:b2现在没有负责人,b2是梦幻西游的业务线
'mhxy'

为中间表创建映射关系
29_python笔记-flask框架_第70张图片
#注:这里 和我写的不一样,我的user_profile_id 是 1、2、3 右键 submit提交

#注:b2是梦幻西游业务线,b2.managers 管理梦幻西游的

Pycharm terminal 命令行下

(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py shell
>>> from model.cmdb_server import BusinessUnit
>>> b1 = BusinessUnit.query.filter_by(business_unit_name="cq").first()	#注:查询 传奇的业务线
>>> b1
<BusinessUnit 1>
>>> dir(b1)	
[……'managers'……]			#注:managers是我建立的映射关系
>>> b1.managers				#注:当前业务线 的负责人  2个
[<UserProfile 3>, <UserProfile 4>]
>>> from model.user import UserProfile			#注:导入 UserProfile
>>> user1 = UserProfile.query.filter_by(user_profile_name=”tlf”).first()
>>> dir(user1)
[……'business_units'……]					
#注:有business_units的记录,因为 为userProfile这个类增加了 business_units  用于反向查询的字段
>>> user1.business_units					#注:user1 负责的业务  
[<BusinessUnit 1>, <BusinessUnit 2>]					
#注:这就是  多对多的关系
>>> user1.business_units[0].business_unit_name	#注:捞取负责的业务的信息   因为user1.business_units返回的是列表
>>> ‘cq’
>>> user1.business_units[1].business_unit_name
>>> ‘mhxy’

2个外键 同时关联到一个表

devopscmdb --> model文件 --> cmdb.server.py文件下
#注:保存 硬件负责人的表结构 和应用负责人的表结构一致,可以把这张表放在一起

#注:添加如下代码
class Asset(db.Model):		#注:做区分,2个外键关联到 同一张表
    ……
    # 业务线,属于什么应用,应用组
    business_id = db.Column(db.ForeignKey("business_unit.business_unit_id"))
    #硬件负责人
    admin_id = db.Column(db.ForeignKey("business_unit.business_unit_id"))

    # 由于这里两个字段链接到同一个表了,不避免区分不开,relationship写在这里
    # 为BusinessUnit表添加了两个字段: asset_businesses, asset_admins
    business_unit = db.relationship("BusinessUnit", backref="asset_businesses",foreign_keys=[business_id])
    admin = db.relationship("BusinessUnit", backref="asset_admins",foreign_keys=[admin_id])
#注:将 2种关系 外键关联id 写在Asset这里,为它每一个关联的外键创建一个BusinessUnit的反向查询字段

class BusinessUnit(db.Model):
    ……
    # assets = db.relationship("Asset",backref="business_units") #注:不要写这个关联 因为有2个字段
#注:这样解决 2个字段 关联同一张表

Pycharm terminal 命令行

#注:生效到数据库
(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py db migrate -m "modify asset"
(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py db upgrade

(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py shell
>>> from model.cmdb_server import Asset, BusinessUnit
>>> sn1 = Asset.query.filter_by(asset_sn="sn1").first()
#注:通过sn1的business_unit属性访问sn1的业务线组
#注:通过sn1的admin属性访问sn1的硬件组

一对一关系

服务器与资产表(一对一关系)

devopscmdb --> model文件 --> cmdb.server.py文件下

# Server与Asset关系
class Asset(db.Model):
    # 一对一关系 server与asset
    server = db.relationship("Server", backref="asset", uselist=False)

class Server(db.Model):
    __tablename__ = "server"
    server_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    asset_id = db.Column(db.Integer, db.ForeignKey('asset.asset_id'))
    server_cpu_count = db.Column(db.Integer)
    server_cpu_cour_count = db.Column(db.Integer)
    server_cpu_model = db.Column(db.String(64))
    server_raid_type = db.Column(db.String(6))
    server_ram_size = db.Column(db.Integer)
    note = db.Column(db.Text)
    create_at = db.Column(db.DateTime())
    update_at = db.Column(db.DateTime())
    status = db.Column(db.Integer())

Pycharm terminal 命令行

(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py shell

>>> from model.cmdb_server import Asset, Server
>>> from model.base import db
>>> sn1 = Asset.query.filter_by(asset_sn="sn1").first()
>>> sn2 = Asset.query.filter_by(asset_sn="sn2").first()
>>> sn1
<Asset 1>
>>> server1 = Server.query.filter_by(asset_id=1).first()
>>> dir(server1)
[…… 'asset', ……]
>>> server1.asset
<Asset 1>
>>> server1.asset.asset_sn
'sn1'
>>> server2 = Server(asset_id=1)			
#注:不是严格的一对一,干扰不到数据库的行为,只是ORM层面的
>>> db.session.add(server2)
>>> db.session.commit()

>>> server3 = Server(server_cpu_model="intel")
>>> db.session.add(server3)
>>> db.session.commit()
>>> server3.asset = sn1			#注:仅仅只是对 设置的这个属性 有效果 (一对一)
……  util.warn(				#注:报错

复制粘贴 创建其他的表

…………
(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py db migrate -m "add server"
(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py db upgrade

29_python笔记-flask框架_第71张图片
#注:数据库 就已经弄好了
#注:准备写接口了

获取服务器数据接口

devopscmdb --> router目录 --> server.py文件

#注:输入以下所有代码
from flask import Blueprint		#注:导入蓝图
from flask_restful import Api, Resource	#注:满足 restful 格式
from model.cmdb_server import Server
from model.base import db

server_bp = Blueprint("server",__name__, url_prefix="/api/cmdb")	#注:创建蓝图

#api 蓝图注册
api = Api(server_bp)

#server视图
class ServerView(Resource):  #注:编写接口
    def get(self):
        server_info = Server.query.all()    #注:获取所有服务器信息
        tmp_list = []		#注:存放数据 json格式 列表里面包字典
        for server in server_info:	#注:序列化的 东西  可以自己写 
            tmp_dict = {}
            tmp_dict["server id"] = server.server_id  #注:本身的属性
            tmp_dict["sn"] = server.asset.asset_sn  #注:添加返回的数据
            tmp_dict["os"] = server.os.os_type      #注:这2个数据 是通过relationship外键 获取
            tmp_list.append(tmp_dict)
        return tmp_list

api.add_resource(ServerView, "/servers")

devopscmdb --> router目录 --> init.py文件

#注:输入以下所有代码
from .server import server_bp

def init_app(app):
    app.register_blueprint(server_bp)   #注册server_bp

devopscmdb --> app.py文件

#注:解除注释
def create_app(config=None):
……
    # 注册蓝图
    from router import init_app
    init_app(app)
……

Pycharm terminal 命令行

(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py runserver -d -h 0.0.0.0 -p 8000
postman下
GET		127.0.0.1:8000/api/cmdb/servers/
#注:获取数据成功,如下
-----------------------------------------------
[
    {
        "server id": 1,
        "sn": "sn1",
        "os": "linux"
    }
]
-----------------------------------------------

devopscmdb --> libs目录 --> response.py文件
#注:步骤 做标准化返回

#注:lib下面一般放 公共的类、库、其他处理的东西
#注:这步 是 做标准化返回
#注:data  形参 最好不要定义成可变数据类型 ,如 [ ] 列表

#统一标准化返回
def generate_reponse(data = None, message = "ok", status_code=10000)
if data is None:
        data = []
    return {
        "status_code":status_code,
        "message":message,
        "data":data
    }

#注:主函数  处理的地方  尽量简洁,具体的操作  尽量放在其他地方

devopscmdb --> libs目录 --> parse.py文件

#注:复制粘贴  解析 步骤
#注:做了一些兼容
from enum import Enum

def get_value(obj, key):
    if obj:				#注:如果有obj
        value = getattr(obj, key)		#注:Python的自省
        return value.name if isinstance(value, Enum) else value	#注:是否是枚举类
#注:是枚举类 就返回 name  否则返回value
    else:
        return ""


def get_value_list(objlist, *keys):
    """返回一个列表数据"""
    if len(keys) == 0:
        return list()
    else:
        result = list()
        for item in objlist:
            if len(keys) == 1:
                result.append(getattr(item, keys[0]))
            else:
                t_result = dict()
                for key in keys:
                    t_result[key] = getattr(item, key)
                result.append(t_result)
        return result


def get_ip(nics, eth):	#注:获取  公/私网 ip
    for nic in nics:
        if eth == nic.nic_name:
            return nic.nic_ipaddr
    return ""
#注:私网ip eth0
#注:公网ip eth1

def server_item_parse(server):
    # print(server)
    result = {
        "id": server.server_id,
        "os": get_value(server.os, "os_version"),	#注:参数1 对象,参数2 属性
        "hostname": get_value(server.asset, "asset_hostname"),
        "sn": get_value(server.asset, "asset_sn"),
        "asset_type": get_value(server.asset, "asset_asset_type"),
        "ip":  get_ip(server.nics, "eth0"),
        "public_ip": get_ip(server.nics, "eth1"),
        "private_ip": get_ip(server.nics, "eth0"),
        "port": 22,
        "idc": get_value(server.asset.idc, "idc_name_cn"),
        "admin_user": get_value_list(server.asset.admin.managers, "user_profile_name") if server.asset.admin else [],
        "region": get_value(server.asset.idc, "idc_region"),
        "state": "true",
        "detail": get_value(server, "note"),
        "create_time": get_value(server, "create_at"),
        "update_time": get_value(server, "update_at"),
    }
    return result


def servers_parse(server):
    if isinstance(server, list):		#注:如果server是一个列表
        # [, ]
        result = [server_item_parse(item) for item in server]		#注:就循环
    else:
        # server = 
        result = server_item_parse(server)
    return result

devopscmdb --> router目录 --> server.py文件

#注:添加如下代码
from flask import Blueprint
from flask_restful import Api, Resource
from model.cmdb_server import Server
from model.base import db
from libs.parse import servers_parse		#注:导入 解析
from libs.response import generate_response	#注:导入 标准化返回

server_bp = Blueprint("server",__name__, url_prefix="/api/cmdb")

#api 蓝图注册
api = Api(server_bp)

#标准化返回: 自己定义
#数据序列化: 自己定义
#定义异常标准化

#server视图
class ServerView(Resource):
    def get(self):
        server_info = Server.query.all()
        return generate_response(servers_parse(server_info))

api.add_resource(ServerView, "/servers/")

Pycharm terminal 命令行

(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py runserver -d -h 0.0.0.0 -p 8000

Postman下

GET		127.0.0.1:8000/api/cmdb/servers/		#注:返回成功
{
    "status_code": 10000,
    "message": "ok",
    "data": [
        {
            "id": 1,
…………
            "update_time": null
        }
    ]
}

29_python笔记-flask框架_第72张图片

GET		127.0.0.1:8000/api/cmdb/servers/

29_python笔记-flask框架_第73张图片
#注:http 出错的话,直接返回一个错误界面,对于接口是不友好的。不是返回错误页面,而是统一标准化 返回。客户端访问 不希望返回页面

from werkzeug.exceptions import HTTPException
HTTPException ctrl+右键  进入 HTTPException类,修改get_body方法

devopscmdb --> libs目录 --> error_code.py文件

#注:libs下一般放 公共的类、库、其他处理的东西、对数据的获取
#注:重写HTTPException ,重写get_body、get_headers方法
from werkzeug.exceptions import HTTPException
class APIException(HTTPException):		#注:定义APIException
    code = 500				#注:http的code
    message = "opps!"
    status_code = 99999			#注:应用程序的code

    def __init__(self, message = None,
        code = None,
        status_code = None,
        headers = None):
        super().__init__()
        if code:				#注:如果code存在
            self.code = code
        if status_code:
            self.status_code = status_code
        if message:
            self.message = message

    def get_body(self, environ=None):
        body = dict(
            message = self.message,
            status_code = self.status_code
        )
        import json
        content = json.dumps(body)
        return content

    def get_headers(self, environ=None):
        return {('content_type','application/json')}

devopscmdb --> libs目录 --> handler.py文件

#注:定义处理异常的模块
from flask_restful import HTTPException
from libs.error_code import APIException

def default_error_hander(ex):		#注:接收一个异常
    if isinstance(ex, APIException):	#注:如果它是APIException异常
        return ex
    if isinstance(ex, HTTPException):	#注:如果它是HTTPException异常
        code = ex.code
        message = ex.description
        status_code = 10001
        return APIException(code = code,message=message, status_code = status_code)
    return APIException()		#注:如果它是其他异常,继承 默认的 APIException

devopscmdb --> router目录 --> server.py文件

from libs.handler import default_error_handler

#指定api接口异常处理函数
#开发环境还是不添加的号
api.handle_error = default_error_handler		#注:指定 重写的 自定义异常

29_python笔记-flask框架_第74张图片
postman下

GET	127.0.0.1:8000/api/cmdb/servers/
#注:捕获到错误异常
{
    "message": "opps!",
    "status_code": 99999
}
#注:序列化异常也可以捕获

嵌套蓝图

#注:蓝图用来注册路由

#/v1/api/cmdb/servers/
#/v1/api/cmdb/memory
#/v1/api/user/

#------> /v2

######
#1、/v1/api
#2、/cmdb/server  /cmdb/memory  /user

#蓝图嵌套
#blueprint 不自带嵌套功能

#注:新建 v1目录
devopscmdb --> router目录 --> v1目录 --> server.py文件

devopscmdb --> libs目录 --> netstable_blueprint.py文件

#注:复制粘贴  网上公认的模板  。做了一个拼接
from flask import Blueprint

class NestableBlueprint(Blueprint):
    def register_blueprint(self, blueprint, **options):
        def deferred(state):
            # state.url_prefix => 自己url前缀 + blueprint.url_prefix => /v3/api/cmdb/
            url_prefix = (state.url_prefix or u"") + (options.get('url_prefix', blueprint.url_prefix) or u"")
            if 'url_prefix' in options:
                del options['url_prefix']
            # app.register_blueprint(blueprint, '/v3/api/cmdb/')
            state.app.register_blueprint(blueprint, url_prefix=url_prefix, **options)
        self.record(deferred)

devopscmdb --> router目录 --> v1目录 --> server.py文件

#注:修改如下
server_bp = Blueprint("server",__name__, url_prefix="/cmdb")	#注:前缀 改为/cmdb

devopscmdb --> router目录 --> v1目录 --> init.py文件

#注册二级蓝图
from libs.netstable_blueprint import NestableBlueprint
from .server import server_bp

v1_bp = NestableBlueprint("v1",__name__, url_prefix="/v1/api/")	#注:
v1_bp.register_blueprint(server_bp)

devopscmdb --> router目录 --> init.py文件

from router.v1 import v1_bp

def init_app(app):	
    app.register_blueprint(v1_bp)	#注:注册v1_bp蓝图

postman下

#注:获取成功
GET	127.0.0.1:8000/v1/api/cmdb/servers/

{
    "status_code": 10000,
    "message": "ok",
    "data": [
        {
            "id": 1,
…………
            "update_time": null
        }
    ]
}

调用接口获取服务器数据

把ip地址 从服务器上去获取
#注:在win pycharm上面开启 8000端口,能够通过API 访问 数据库。 Server端 B主机(linux) requests 访问 client端8000端口,获取A机器的ip地址,scp跑在A机器上 获取A机器的信息

(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py runserver -d -h 0.0.0.0 -p 8000

#注:通过接口 获取ip信息。在脚本里导入 requests模块
#注:从接口获取数据,客户端 在中控机 linux,服务器是Windows 。访问Windows的接口ip网站,获取它的ip地址信息

pycham 编辑远程脚本

参考博文 https://www.cnblogs.com/xiao-apple36/p/8587086.html
菜单栏Tools --> Deployment --> Configuration…
29_python笔记-flask框架_第75张图片
29_python笔记-flask框架_第76张图片
#注:Automatic Upload (always) #注:自动上传
#注:Browse Remote Host #注:浏览远程主机

数据库里 存放ip的字段 数据设置
#注:主机A的ip
29_python笔记-flask框架_第77张图片
#注:需要添加 nic_name 为”eth0” ,因为是靠它 寻找的

cmdbclient2020-linux --> settings.py文件

#注:修改url
API = "http://192.168.0.61:8000/v1/api/cmdb/servers/"

#注:修改为 192.168.0.61

#注:查看Windows的ip地址
29_python笔记-flask框架_第78张图片

#注:在linux机器(客户端)上 检测 网络连通性
[root@cPen_B cmdbclient2020-linux]# ping 192.168.0.61			#注:ping通
[root@cPen_B cmdbclient2020-linux]# telnet 192.168.0.61 8000		#注:telnet 8000 通

cmdbclient2020-linux --> servers_info.py文件

import requests

def get_servers():
    """
    从cmdb-api获取要采集哪些机器的数据数据
    :return:
    """
    # 从接口获取数据
    from settings import API
    content = requests.get(API).text
    datas = json.loads(content)["data"]  #注:从字符串转换成json对象
    server_list = {}
    for data in datas:  #注:循环获取 里面的内容
        server_list[data["sn"]] = data["ip"]
    # server_list = {
    #     "sn1":"192.168.0.65",
    #     "sn2":"192.168.0.36"
    # }
    return server_list

def copy_file(host,path):		#注:修改端口 2233
    import os
    os.system("scp -rq -P 2233 cmdbclient2020 {host}:{path}".format(host=host, path=path))

def ssh2(ip,sn,run_cmd,env_cmd):	#注:修改端口 2233
    try:	……
        ssh.connect(ip,2233,timeout=5)

#注:服务监听 要监听到 本机所有ip上面 0.0.0.0 ;监听到 127.0.0.1 外面访问不了

Linux下

[root@cPen_B cmdbclient2020-linux]# python3 servers_info.py 
Begin......							#注:获取成功
{'sn1': {'status': True, 'message': '', …… 'netmask': '255.255.255.0'}}}}}}
End......
[root@cPen_B cmdbclient2020-linux]# 

优化:增加API授权

#注:使用APIToken 保存授权密钥 授权的key值

#注:模型放在devopscmdb --> model目录 --> user.py文件下

devopscmdb --> model目录 --> user.py文件下

#注:输入以下全部代码
from libs.enums import MethodType

class UserProfile(db.Model):
    ……

api_token_permissions = db.Table("api_token_permissions",		#注:中间表
                                 db.Column("api_token_id",db.ForeignKey("api_token.api_token_id")),
                                 db.Column("api_permission_id",db.ForeignKey("api_permission.api_permission_id")))

class APIToken(db.Model):
    __tablename__ = "api_token"
    api_token_id = db.Column(db.INTEGER, primary_key=True, autoincrement=True)
    api_token_appid = db.Column(db.String(64))
    api_token_secretkey = db.Column(db.String(64))
    user_profile_id = db.Column(db.ForeignKey('user_profile.user_profile_id'))	#注:加入外键
    permissions = db.relationship("APIPermission",
                                  secondary = api_token_permissions,
                                  backref = "api_tokens")
    note = db.Column(db.Text)			#注:加入 公共字段
    create_at = db.Column(db.DateTime())
    update_at = db.Column(db.DateTime())
    status = db.Column(db.Integer())

class APIPermission(db.Model):		#注:更细的权限,允许 哪些接口、方法 访问
    __tablename__ = "api_permission"
    api_permission_id = db.Column(db.INTEGER, primary_key=True, autoincrement=True)
    api_permission_url = db.Column(db.String(256))
    api_permission_method_type = db.Column(db.Enum(MethodType))	#注:枚举型
    note = db.Column(db.Text)			#注:加入 公共字段
    create_at = db.Column(db.DateTime())
    update_at = db.Column(db.DateTime())
    status = db.Column(db.Integer())

#注:程序 肯定有很多的url,一个url可以给很多接口去使用,一个接口可以有很多个url,多对多的关系

pycharm terminal命令行

#注:创建表
(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py db migrate -m "add api token"
(venv) E:\web_cpen\flask_proj\devopscmdb>python manage.py db upgrade

pycharm MySQL 插入数据
29_python笔记-flask框架_第79张图片
mysql
#注:中间表
mysql
devopscmdb --> libs目录 --> authorize.py文件下

#注:创建新的认证模块
from flask import request
from model.user import APIToken
from .error_code import APIAuthorizedException	#注:用户认证失败的自定义异常
from hashlib import md5			#注:导入md5

def api_authorize():				#注:复制粘贴以下全部代码
    params = request.args
    appid = params.get('appid')
    salt = params.get('salt')
    sign = params.get('sign')
    api_token = APIToken.query.filter_by(api_token_appid=appid).first()
    if not api_token:			#注:第1步 appid的认证
        raise APIAuthorizedException(message="认证失败!没有查找到api_token")
    has_permission(api_token, url=request.path, method=request.method)
    user_appid = api_token.api_token_appid
    user_secretkey = api_token.api_token_secretkey
    user_sign = user_appid + salt + user_secretkey
    m1 = md5()
    m1.update(user_sign.encode(encoding='utf8'))
    user_sign = m1.hexdigest()
    if sign != user_sign:
        raise APIAuthorizedException()
    else:
        return True

def has_permission(api_token, url, method):	#注:复制粘贴以下全部代码
    """权限该api是否有指定url和指定方法的权限"""
    # 从服务端查找appid及对应的秘钥
    mypermission = method + url
    all_permissions = [permission.api_permission_method_type.name + permission.api_permission_url for permission in
                       api_token.permissions]
    if mypermission not in all_permissions:
        raise APIAuthorizedException(message="没有当前接口的权限")
    return True

devopscmdb --> libs目录 --> error_code.py文件下

#注:认证失败的返回信息
class APIAuthorizedException(APIException):	#注:用户认证失败的自定义异常,继承APIException
    message = "用户认证失败"
    status_code = 10004			#注:给应用的返回码
    code = 401				#注:给http的返回码

devopscmdb --> v1目录 --> server.py文件下

#注:加入认证
from libs.authorize import api_authorize

class ServerView(Resource):
    def get(self):
        api_authorize()
        ……

postman下
GET 127.0.0.1:8000/v1/api/cmdb/servers/
29_python笔记-flask框架_第80张图片
cmdbclient2020-linux --> settings.py文件下

#注:修改如下代码
API = "http://192.168.0.61:8000/v1/api/cmdb/servers/"
APPID = 'cmdbclient'
SECRETKEY = '123456'
VERSION = "v1"
KEY = '299095cc-1330-11e5-b06a-a45e60bec08b'

cmdbclient2020-linux --> servers_info.py文件下

#注:添加如下代码
from hashlib import md5

def get_servers():
    """
    从cmdb-api获取要采集哪些机器的数据数据
    :return:
    """
    # 从接口获取数据
    from settings import API, APPID, SECRETKEY
    salt = random.randint(32768, 65536)
    sign = APPID + str(salt) + SECRETKEY
    m1 = md5()
    m1.update(sign.encode(encoding='utf8'))
    sign = m1.hexdigest()
    params = {			#注:认证通过 传递这3个参数,认证不通过 不传递这3个参数
        "appid": APPID,
        "salt": salt,
        "sign": sign,
    }
    content = requests.get(API,params=params).text
    datas = json.loads(content)["data"]  #注:从字符串转换成json对象
    server_list = {}
    for data in datas:  #注:循环获取
        server_list[data["sn"]] = data["ip"]
    # server_list = {
    #     "sn1":"192.168.0.65",
    #     "sn2":"192.168.0.36"
    # }
    return server_list

#注:服务端 颁布密钥 给客户端 去使用

linux下

#注:打印出来了
[root@cPen_B cmdbclient2020-linux]# python3 servers_info.py 
Begin......
{'sn1': {'status': True, 'message': ……'netmask': '255.255.255.0'}}}}}}
End......

Postman下

#注:获取成功
127.0.0.1:8000/v1/api/cmdb/servers/?appid=cmdbclient&salt=42291&sign=ab3953c19b0516b2c38f5d79d07f05e7

29_python笔记-flask框架_第81张图片

你可能感兴趣的:(Python笔记,python,flask)