测试开发之路--Flask 之旅 (三):数据库

背景

通过前两次的努力,我们对环境有了增删查改以及部署和查看日志的能力。 现在已经处于将就可用的状态。但其实还差了很重要的东西,就是权限的管理。 因为不能说每个用户上来都能随便的重启和删除环境吧,太容易出事故了。所以我们想起码有最基本的隔离性。

Flask 扩展模块

上一次我们使用了 Flask-WTF 这个针对于表单的扩展模块。 这次为了加入权限管理,我们需要再安装以下这些扩展模块。

  • Flask-SQLAlchemy
  • Flask-MySQLdb
  • Flask-Security
  • Flask-Login

涉及到权限就需要有用户的概念,我们需要数据库来存储我们用户和环境的信息,所以要使用 Flask-SQLAlchemy 和 Flask-MySQLdb(我使用的是 mysql)。为了解决用户登录的问题引入 Flask-Login, 为了生成角色和权限的概念引入 Flask-Security。这里解释一下 Flask-Security,它无法单独使用,更像是 Flask-Login 和 Flask-SQLAlchemy 的扩展。 是为了增强他们的权限控制能力而存在的。所以再加入 Flask-Security 后,Flask-SQLAlchemy 和 Flask-Login 的使用方式都跟以前有些不太一样了。接下来我会一个一个介绍

创建数据库

我们先解决数据库的问题吧。 再安装好上面的所有模块后 (注:缺一不可),我们首先要在数据库中创建名叫 env 的库。供我们使用


再启动 app 前设置一下数据库参数:

# 连接mysql数据库的配置
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:root@172.27.1.221:3306/env'

然后我们创建一个 models.py

from __init__ import *
from flask_security import RoleMixin, UserMixin# Create database connection object
db = SQLAlchemy(app)# Define models
roles_users = db.Table('roles_users',db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))class Role(db.Model, RoleMixin):id = db.Column(db.Integer(), primary_key=True)name = db.Column(db.String(80), unique=True)description = db.Column(db.String(255))class User(db.Model, UserMixin):id = db.Column(db.Integer, primary_key=True)email = db.Column(db.String(255), unique=True)password = db.Column(db.String(255))active = db.Column(db.Boolean())confirmed_at = db.Column(db.DateTime())roles = db.relationship('Role', secondary=roles_users,backref=db.backref('users', lazy='dynamic'))envs = db.relationship('Env', backref=db.backref('user'))class Env(db.Model):def __init__(self, name, user_id):self.name = nameself.user_id = user_idid = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(255), unique=True)user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

看到这么一坨代码可能有点懵逼。我来解释解释吧。 Flask-SQLAlchemy 是一个 ORM 框架,它组织了数据库表到类的影射。所以我们可以使用管理这些类对象的方式管理数据库。 你看到上面我们创建了 User,Env 和 Role 类,分别对应了用户,环境和角色。它们都集成了 db.Model。我们把 SQLAlchemy(app) 赋值给 db,这是初始化我们数据库的方式。 然后你会发现 User 和 Role 又分别继承了 UserMixin 和 RoleMixin。暂时可以不用管它, 这个是 Flask-Security 的东西。 表之间的关系我用外键来定义,额,虽然我知道外键的性能不好。开发人员几乎从来不用。但是我这么个小玩意就不在意这些有的没的了,一共就没几条数据。看到上面对 roles_users 的定义可能大家会觉得有点懵逼。 这是 SQLAlchemy 处理多对多关系的方式:产生一张中间表。 SQLAlchemy 处理表关系的方式就是 relationship 方法。这部分内容有点多~~ 具体详细的 API 定义请大家参考官方文档:快速入门 — Flask-SQLAlchemy 2.0 documentation

接下来我们只需要调用下面的代码就会在数据库创建出这些表了。

db.create_all()

3 张表加一个 roles_users 的表。

数据库操作

现在我们有表了,我们想初始化一些数据。 所以我们先看看一些基础的操作。
例如我们想创建一个环境信息,我们可以这么做:

env = Env()
env.name = 'test01'
env.user_id = '1'
db.session.add(env)
db.session.commit()

这是添加一条数据的方式。 只要是增删改的操作,只要没有运行 db.session.commit(), 就不会真正的入库。 这是为了保证事务性。增加了一条数据后,现在我们来查询数据:

env = Env.query.filter_by(name='test01').first()

由于我们 model 的类都继承了 db.Models。 所以我们可以使用一些方法来进行查询。 也可以使用一些复杂一点的查询方式,如下:

Env.query.filter(name.endswith('01')).all()

使用 Flask-Security 创建 User 和 Role

OK,现在我们看看一开始没有说明的 UserMixin 和 RoleMixin。上面说过他们是 Flask-Security 针对 Flask-SQLAlchemy 做的扩展。通过 Flask-Security 我们可以很方便的管理用户权限但是它对我们的 model 有一定的要求。请看下图:


就是说我们一定要有 User 和 Role 这两张表以及包含相应的字段。 这样 Flask-Security 才能够帮助我们生成权限管理的解决方案。举个简单的例子,通过使用 Flask-Security, 我们可以用下面的方式创建用户:

from urls import db, User,  Role
from flask_security import SQLAlchemyUserDatastore, Security
# Setup Flask-Security
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)
db.create_all()admin = user_datastore.create_user(email='admin@4paradigm.com', password='admin')
# 生成普通用户角色和admin用户角色
user_datastore.create_role(name='User', description='Generic user role')
admin_role = user_datastore.create_role(name='Admin', description='Admin user role')
# 为admin添加Admin角色
user_datastore.add_role_to_user(admin, admin_role)
db.session.commit()

上面是我初始化数据库的代码。 为了能使用 Flask-Security。我们使用 SQLAlchemyUserDatastore 来封装 db, 使用 Security 封装我们的 web app。 接下来就可以看到我们使用很方便的方式创建 user 和 role。 并且使用 add_role_to_user 的方式为一个用户添加角色。Flask-Security 为我们提供了很多有用的方法。请看下面的截图:

更多的 API 请查看官方文档:API — Flask-Security 3.0.0 documentation

未完待续

上面说的这些方法都可以很有效的管理我们数据库中 User 和 Role 的关系。再下一篇帖子中,我会详细介绍如何使用 Flask-Security 来做权限控制。今天太晚了~ 先这样吧

另外欢迎大家加入我的知识星球, 最近正在更新手把手教你人工智能测试系列。

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

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

相关文章

堆和堆排序【数据结构】

目录 一、堆1. 堆的存储定义2. 初始化堆3. 销毁堆4. 堆的插入向上调整算法 5. 堆的删除向下调整算法 6. 获取堆顶数据7. 获取堆的数据个数8. 堆的判空 二、Gif演示三、 堆排序1. 堆排序(1) 建大堆(2) 排序 2.Topk问题 四、完整代码1.堆的代码Heap.cHeap.htest.c 2. 堆排序的代码…

Spring中用Mybatis注解查询映射多个对象

1.映射写法如下 SelectProvider(type UserGroupMapper.class, method "getOrigins")Results({Result(property "id", column "id"),Result(property "groupId", column "groupId"),Result(property "resId&qu…

std::set自定义比较器

set的比较器可用于&#xff0c;insert(),count(),find()等函数 如下&#xff1a; 1. 使用lamda表达式 auto cmp [](int a, int b) { return … }; std::set<int, decltype(cmp)> s(cmp); 2. 结构体的重载操作符&#xff08;&#xff09; 3. 使用普通函数作为比较器

排序(2)——选择排序

三、选择排序 1.简介 选择排序主要采取的排序策略就是选择&#xff0c;在拿到待排序数组后&#xff0c;程序会一遍遍地遍历未排序部分数组&#xff0c;在每一次的遍历过程中会找到最小的元素&#xff0c;并在遍历完成后换到未排序数组部分的最左侧。如此循环往复&#xff0c;每…

Qt QPlainTextEdit高亮显示当前行

Qt QPlainTextEdit高亮显示当前行 文章目录 Qt QPlainTextEdit高亮显示当前行摘要错误的代码正确的代码QTextEdit::ExtraSelection 关键字&#xff1a; Qt、 QPlainTextEdit、 QTextBlock、 ExtraSelection、 GPT 摘要 今天要在说一下GPT&#xff0c;当下如果你还不会用G…

【Python从入门到进阶】48、当当网Scrapy项目实战(一)

接上篇《47、Scrapy Shell的了解与应用》 上一篇我们学习了Scrapy终端命令行工具Scrapy Shell&#xff0c;并了解了它是如何帮助我们更好的调试爬虫程序的。本篇我们将正式开启一个Scrapy爬虫项目的实战&#xff0c;对当当网进行剖析和抓取。 一、当当网介绍 当当网成立于199…

【数据结构】二叉搜索树的模拟实现

目录 1、概念 2、模拟实现 2.1、查找 2.2、插入 2.3、删除&#xff08;难点&#xff09; 3、性能分析 4、完整代码 1、概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 若它的左子树不为空&#xff0c;则左子树上所有…

C语言实现基础数据结构——顺序表

目录 顺序表 顺序表和数组 顺序表的分类 静态顺序表 动态顺序表 静态顺序表和动态顺序表的比较 动态顺序表的实现 主要实现功能 顺序表的初始化 顺序表的销毁 顺序表的打印 顺序表的尾部插入 顺序表的头部插入 顺序表的尾部删除 顺序表的头部删除 顺序表的指定…

如何使用docker compose安装APITable并远程访问登录界面

文章目录 前言1. 部署APITable2. cpolar的安装和注册3. 配置APITable公网访问地址4. 固定APITable公网地址 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的 人工智能学习网站&#xff0c; 通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。 …

Java基础常见面试题总结(下)

常见的Exception有哪些&#xff1f; 常见的RuntimeException&#xff1a; ClassCastException //类型转换异常IndexOutOfBoundsException //数组越界异常NullPointerException //空指针ArrayStoreException //数组存储异常NumberFormatException //数字格式化异常ArithmeticE…

Mysql-InnoDB-数据落盘

概念 1 什么是脏页&#xff1f; 对于数据库中页的修改操作&#xff0c;则首先修改在缓冲区中的页&#xff0c;缓冲区中的页与磁盘中的页数据不一致&#xff0c;所以称缓冲区中的页为脏页。 2 脏页什么时候写入磁盘&#xff1f; 脏页以一定的频率将脏页刷新到磁盘上。页从缓冲区…

教你一招,测试人员如何通过AI提高工作效率!

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

C++仿函数、万能头文件、transform学习

这是网上的一个代码,里面的一些东西以前没用过; #include <bits/stdc++.h> using namespace std;// A Functor class increment { private:int num; public:increment(int n) : num(n) { }int operator () (int arr_num) const {return num + arr_num;} };// Driver …

【破事水】Java Gradle 无法引入同名不同版本的两个包

此问题水于 2024 年 01 月&#xff0c;假如后面 gradle 出了什么好方法能解决这个问题&#xff0c;家祭无忘告乃翁&#xff0c;提前谢过看到这篇的各位大佬了。 结论 先说结论&#xff0c;Java 因为包名定义等原因&#xff0c;对同名包在编译时只能编译一个版本&#xff0c;具…

Kafka高级_生产者ACk机制数据一致性问题

Kafka高级_生产者ACk机制&数据一致性问题 目录需求&#xff1a; 设计思路实现思路分析1.Kafka高级_生产者ACk机制2.Kafka高级数据一致性问题 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c…

微信小程序开发学习笔记《13》WXS脚本

微信小程序开发学习笔记《13》WXS脚本 博主正在学习微信小程序开发&#xff0c;希望记录自己学习过程同时与广大网友共同学习讨论。建议仔细阅读对应官方文档 一、WXS介绍 WXS ( WeiXin Script)是小程序独有的一套脚本语言&#xff0c;结合WXML&#xff0c;可以构建出页面的…

CSDN COC·北京开发者社区2023年度聚会

CSDN COC北京开发者社区2023年度聚会 年末盛会&#xff1a;北京开发者社区2023年度聚会 1. 活动背景&#x1f389;2. 活动议程3. 活动亮点介绍 &#x1f31f;3.1 主理人开场破冰3.2 话题讨论&#xff1a;3.3 城市社区介绍、回顾与展望 &#x1f3d9;️4. 活动留念5.活动总结 博…

浅出深入-机器学习

文章目录 一、K近邻算法1.1 先画一个散列图1.2 使用K最近算法建模拟合数据1.3 进行预测1.4 K最近邻算法处理多元分类问题1.5 K最近邻算法用于回归分析1.6 K最近邻算法项目实战-酒的分类1.6.1 对数据进行分析1.6.2 生成训练数据集和测试数据集1.6.3 使用K最近邻算法对数据进行建…

计算机网络——路由信息协议 (RIP) 实验

1.实验题目 实验五&#xff1a;路由信息协议 (RIP) 实验 2.实验目的 &#xff08;1&#xff09;了解RIP的相关知识以及原理。 &#xff08;2&#xff09;掌握RIP路由的配置方法。 3.实验任务 (1) 路由器的基本配置&#xff1a;设置路由器接口 IP 地址。 (2) 根据以上拓扑…

Python Tornado 实现SSE服务端主动推送方案

一、SSE 服务端消息推送 SSE 是 Server-Sent Events 的简称&#xff0c; 是一种服务器端到客户端(浏览器)的单项消息推送。对应的浏览器端实现 Event Source 接口被制定为HTML5 的一部分。相比于 WebSocket&#xff0c;服务器端和客户端工作量都要小很多、简单很多&#xff0c…