首页 > 其他分享 >使用flask入门一个测试平台01-模型设计

使用flask入门一个测试平台01-模型设计

时间:2022-10-03 21:47:03浏览次数:57  
标签:01 入门 flask db Char Column Integer id String

前言

​ 在去年的年中,我一时冲动写了一个基于flask开发的测试平台,然后把服务托管在了腾讯云上,本来想是写文章分享的怎么开发的,但是一直没有写。如今一年已经过去了,服务器也是到期了,演示网址也是无法访问了。
正好现在是国庆假期,一天好似虚度人生,就敲起键盘写一点我的这个测试平台。

​ 首先肯定有一个名称,因为我羡慕自由的鸟儿飞翔在天空,所以我给起的名字是flytest。好了,名称就是这个样子了。继续看吧。

技术栈

​ 本项目是一个前后端不分离的jinja2渲染形式的平台,所以前端页面我使用的是bootstrap4。别问我为啥不用VUE,问就是不会。

环境 用途
bootstrap4 页面显示
flask 后端使用的框架
celery 测试用例执行
MySQL 测试数据的存储
redis 缓存
APScheduler 定时任务开发
nginx+gunicorn+supervisor 环境部署

平台结构

​ 测试平台大概分为这么几个模块,每一个模块都有相关的作用,但是每个模块之间又有着很强的关联性。

模块 作用
项目管理 对不同项目进行管理
环境管理 管理测试环境和生产环境
测试管理 添加用例并进行测试
任务列表 测试的任务和任务结果显示
定时任务 定时任务列表
最新报告 显示最近的测试结果
问题管理 显示最新的问题
结果趋势 通过折线图查看最新的测试情况

平台截图

简陋的登录页
image-20210106165027396
注册页面
image
主界面
image-20210905212400769
测试管理页
image

流程设计

​ 以下是我对于这个测试平台的流程构想,其中人员管理部分是采用邮箱注册并激活的形式进行处理。

测试平台操作流程设计图

数据库设计

在这个模型文件中使用了两个flask第三方库,分别是flask-loginflask-avatars

# 需要做这样的导入
from flask_login import UserMixin, current_user
from flask_avatars import Identicon
用户表
字段名称 类型 解释 关系
id Integer 主键
email Char(128) 邮箱地址
username Char(128) 用户名称
password_hash Char(128) 密码
avatar_s Char(64) 头像
avatar_m Char(64) 头像
avatar_l Char(64) 头像
is_acticed Boolean 是否激活
product 关联项目表
apitest 关联测试表
产品管理表
字段名称 类型 解释 关系
name Char(128) 产品名称
desc Char(512) 产品描述
tag Char 产品类型
user_id Integer 产品负责人
测试地址管理表
字段名称 类型 解释 关系
name Char(128) 地址名称
url Char(512) 地址
product_id Integer 产品ID
测试表
字段名称 类型 解释 关系
name Char(128) 地址名称
results Integer 测试结果
task_id Char(256) 任务ID
user_id Integer 用户ID
测试步骤
字段名称 类型 解释 关系
name Char(128) 请求名称
method Char(16) 请求方法
route Char(512) 请求路径
headers Text 请求头
request_data Text 请求数据
expected_result Char(512) 预期值
expected_regular Char(512) 预期正则
request_extract Char(512) 请求验证
response_extract Char(512) 返回验证
results Text(2048) 结果
报告
字段名称 类型 解释 关系
types Integer 1普通任务2定时任务
task_id Char(256) 任务ID
name Char(256) 名称
result Char(2048) 结果
问题库
字段名称 类型 解释 关系
task_id Char(256) 任务ID
casename Char(256) 用例名称
stepname Char(512) 步骤名称
request Text 请求
detail Text 详情
level Char(10) 级别
任务结果
字段名称 类型 解释 关系
task_id Char(256) 任务ID
name Char(512) 任务名称
hostname Char(512) 主机名称
params Char(512) 任务参数
result Text 结果
traceback Text 异常详情
定时任务
字段名称 类型 解释 关系
task_id Char(256) 任务ID
test_name Char(128) 任务名称
func_name Char(256) 任务执行函数名
trigger Char(64) 执行类型
args Char(128) 参数
kwargs Char(128) 关键字参数
max_instances
times
misfire_grace_time
next_run_time Datetime 下次运行时间
start_date Date 开始日期
is_active Boolean 是否激活
product_id Integer 项目ID

​ 以上我们把表结构大概设计好了,看看实际的sqlalchemy模型文件吧:

from datetime import datetime

from werkzeug.security import generate_password_hash, check_password_hash
from flask import session, current_app
from flask_login import UserMixin, current_user
from flask_avatars import Identicon
from app.extensions import db, cache


class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(128), unique=True)
    username = db.Column(db.String(128))
    password_hash = db.Column(db.String(128))
    avatar_s = db.Column(db.String(64))
    avatar_m = db.Column(db.String(64))
    avatar_l = db.Column(db.String(64))
    created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    updated = db.Column(db.DateTime, onupdate=datetime.utcnow,
                        default=datetime.utcnow)
    is_deleted = db.Column(db.Boolean, default=False, nullable=False)
    is_active = db.Column(db.Boolean, default=False, nullable=False)

    products = db.relationship('Product', back_populates='user')
    apitests = db.relationship('Apitest', back_populates='user')

    def __init__(self, *args, **kwargs):
        super(User, self).__init__(*args, **kwargs)
        self.generate_avatar()

    def generate_avatar(self):
        avatar = Identicon()
        filenames = avatar.generate(text=self.email)
        self.avatar_s = filenames[0]
        self.avatar_m = filenames[1]
        self.avatar_l = filenames[2]
        db.session.commit()

    @property
    def password(self):
        return AttributeError("password is only readable")

    @password.setter
    def password(self, pwd):
        self.password_hash = generate_password_hash(pwd)

    def verify_password(self, pwd):
        return check_password_hash(self.password_hash, pwd)

    def __repr__(self):
        return self.username

    def __str__(self) -> str:
        return self.username


class Product(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128))
    desc = db.Column(db.Text)
    tag = db.Column(db.String(32))
    created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    updated = db.Column(db.DateTime, onupdate=datetime.utcnow,
                        default=datetime.utcnow)
    is_deleted = db.Column(db.Boolean, default=False, nullable=False)

    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    user = db.relationship('User', back_populates='products')

    apiurls = db.relationship('Apiurl', back_populates='product')
    apitests = db.relationship('Apitest', back_populates='product')
    reports = db.relationship('Report', back_populates='product')
    bugs = db.relationship('Bug', back_populates='product')
    works = db.relationship('Work', back_populates='product')

    def __repr__(self):
        return '<Product %s>' % self.name

    @staticmethod
    def get_product(pk):
        if pk is not None:
            session["current_product"] = pk
        pk = session.get("current_product", None)
        product = Product.query.filter_by(id=pk, user=current_user, is_deleted=False).one_or_none() or Product.query.filter_by(user=current_user, is_deleted=False).first()
        return product


class Apiurl(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128))
    url = db.Column(db.String(512))
    created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    updated = db.Column(db.DateTime, onupdate=datetime.utcnow,
                        default=datetime.utcnow)
    is_deleted = db.Column(db.Boolean, default=False, nullable=False)

    product_id = db.Column(db.Integer, db.ForeignKey('product.id'))
    product = db.relationship('Product', back_populates="apiurls")
    apisteps = db.relationship('Apistep', back_populates='apiurl')

    def __repr__(self):
        return '<Url %s>' % self.name


class Apitest(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64))
    results = db.Column(db.Integer, default=-1)
    task_id = db.Column(db.String(255), index=True)
    created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    updated = db.Column(db.DateTime, onupdate=datetime.utcnow,
                        default=datetime.utcnow)
    is_deleted = db.Column(db.Boolean, default=False, nullable=False)

    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    user = db.relationship('User', back_populates='apitests')
    product_id = db.Column(db.Integer, db.ForeignKey('product.id'))
    product = db.relationship('Product', back_populates='apitests')
    apisteps = db.relationship('Apistep', back_populates='apitest')

    def __repr__(self):
        return '<ApiTest %s>' % self.name


class Apistep(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128))
    method = db.Column(db.String(16))
    route = db.Column(db.String(512))
    headers = db.Column(db.Text)
    request_data = db.Column(db.Text, nullable=True)
    expected_result = db.Column(db.String(512))
    expected_regular = db.Column(db.String(512), nullable=True)
    request_extract = db.Column(db.String(512))
    response_extract = db.Column(db.String(512))
    status = db.Column(db.Integer, default=-1)
    results = db.Column(db.Text(2048), nullable=True)
    created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    updated = db.Column(db.DateTime, onupdate=datetime.utcnow,
                        default=datetime.utcnow)
    is_deleted = db.Column(db.Boolean, default=False, nullable=False)

    apiurl_id = db.Column(db.Integer, db.ForeignKey('apiurl.id'))
    apiurl = db.relationship('Apiurl', back_populates='apisteps')
    apitest_id = db.Column(db.Integer, db.ForeignKey('apitest.id'))
    apitest = db.relationship('Apitest', back_populates='apisteps')
    report_id = db.Column(db.Integer, db.ForeignKey("report.id"))
    report = db.relationship('Report')

    def __repr__(self):
        return '<ApiStep %s>' % self.name


class Report(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    types = db.Column(db.Integer, default=1)  # 1是普通任务 2是定时任务
    task_id = db.Column(db.String(256), index=True)
    name = db.Column(db.String(256), default="NULL")
    result = db.Column(db.String(2048))
    status = db.Column(db.Integer, default=-1)
    created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    updated = db.Column(db.DateTime, onupdate=datetime.utcnow,
                        default=datetime.utcnow)
    is_deleted = db.Column(db.Boolean, default=False, nullable=False)

    apistep = db.relationship('Apistep', uselist=False)
    product_id = db.Column(db.Integer, db.ForeignKey('product.id'))
    product = db.relationship('Product', back_populates='reports')


class Bug(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    task_id = db.Column(db.String(256), index=True)
    casename = db.Column(db.String(256))
    stepname = db.Column(db.String(512))
    request = db.Column(db.Text)
    detail = db.Column(db.Text)
    status = db.Column(db.Integer)
    level = db.Column(db.String(10), default='一般')
    created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    updated = db.Column(db.DateTime, onupdate=datetime.utcnow,
                        default=datetime.utcnow)
    is_deleted = db.Column(db.Boolean, default=False, nullable=False)

    product_id = db.Column(db.Integer, db.ForeignKey('product.id'))
    product = db.relationship('Product', back_populates='bugs')

    def __repr__(self):
        return '<BUG FOR %S>' % self.stepname


class Work(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    task_id = db.Column(db.String(256), index=True)
    name = db.Column(db.String(512))
    hostname = db.Column(db.String(512))
    params = db.Column(db.String(512))
    status = db.Column(db.Text)
    result = db.Column(db.Text)
    traceback = db.Column(db.Text)
    created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)

    product_id = db.Column(db.Integer, db.ForeignKey('product.id'))
    product = db.relationship('Product', back_populates='works')

    def __repr__(self):
        return self.task_id


class CronTabTask(db.Model):
    """定时任务"""
    id = db.Column(db.Integer, primary_key=True)
    task_id = db.Column(db.String(256), index=True)
    test_name = db.Column(db.String(128))
    func_name = db.Column(db.String(256))
    trigger = db.Column(db.String(64))
    args = db.Column(db.String(128))
    kwargs = db.Column(db.String(128))
    max_instances = db.Column(db.Integer)
    times = db.Column(db.String(128))
    misfire_grace_time = db.Column(db.Integer)
    next_run_time = db.Column(db.String(256))
    start_date = db.Column(db.String(256))

    is_active = db.Column(db.Boolean, default=True, nullable=False)
    product_id = db.Column(db.Integer)

以上就是模型文件的所有数据了,感觉第一篇应该讲一下怎么搭建环境,好了,先写这么多吧。

标签:01,入门,flask,db,Char,Column,Integer,id,String
From: https://www.cnblogs.com/wxhou/p/16751339.html

相关文章

  • Flask 学习-84.Flask-SQLAlchemy 一对多关系级联删除
    前言一对多关系,当删除主表数据的时候,关联表数据一起删除掉一对多模型设计classPerson(db.Model):id=db.Column(db.Integer,primary_key=True)name=db.......
  • 如何在阿里云服务器搭建flask
    下载flask不用我多说了,pip3installflask 今天在阿里云的服务器上测试一个Flask程序,命名指定了ip:0.0.0.0,port:5000,但是外网IP确怎么也访问不了网页 上网上好个查,发......
  • Spring Cloud入门
    简介微服务是由springboot开发的一个个的模块,是一个个独立的进程springcloud是微服务全家桶springcloud通过网关调用多个负载均衡(微服务)springboot是以数字作为版本,spring......
  • Cit 入门操作笔记
    Git操作入门   Git  是一个快速、可扩展的 分布式版本控制系统 ,它具有极为丰富的命令集,对内部系统提供了高级操作和完全访问.Git与你熟悉的大部分版本控制系统的......
  • 【树上背包】洛谷 P4516 [JSOI2018] 潜入行动
    P4516[JSOI2018]潜入行动省选/NOI-、树上背包计数题意略设状态为\(dp[u][j][0/1][0/1]\),u点子树放了j个装置,u点有没有放装置,u点有没有被监听的方案数。对......
  • day04-MySQL常用函数01
    5.MySQL常用函数5.1合计/统计函数5.1.1合计函数-countcount返回行的总数Selectcount(*)|count(列名)fromtable_name [WHEREwhere_definition]练习--统计......
  • 20201322陈俊池学习笔记5
    第十一章EXT2文件系统一、知识点归纳11.1EXT2文件系统Linux一直将EXT2作为默认文件系统。EXT3是EXT2的扩展。EXT3中增加的主要内容是一个日志文件,他将文件系统的变更......
  • 2019上半年(上午)网络工程师试题解析
    2019上半年(上午)网络工程师试题解析1.计算机执行指令的过程中,需要由( )产生每条指令的操作信号,并将信号送往相应的部件进行处理,以完成指定的操作。A.CPU的控制器 B.CPU的......
  • Latex编码错误:inputenc Error: Unicode char ́ (U+0301)
    遇到这个报错信息,大概率是因为你的bib文件或者tex正文里出现了这个长得像e的字符:é解决办法很简单,你只需要在tex最前面(就是\usepackage那一坨地方)加上这么一行声明就好了......
  • 20201206韩进学习笔记5
    EXT2文件系统EXT2文件系统数据结构通过mkfs创建虚拟磁盘在Linux下,命令mke2fs[-bblksize-Ninodes]deviceblocks在设备上创建一个带有nblocks个块和inode......