3.平台数据模型设计
大约 5 分钟学习笔记测试平台开发
一、 数据库配置
1. 搭建 MySQL 数据库
2. 创建数据库
CREATE DATABASE `course_autotp` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
3. 安装 pymysqlclient 模块
# 链接 mysql 数据库需要此模块,此模块安装不了的,参考附录的方法
pip install mysqlclient
4. 配置 数据库参数
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'course_autotp',    # 数据库名称
        'USER': 'root',     # 用户名
        'PASSWORD': 'xxxx',   # 密码
        'HOST': '158.247.205.137',
        'PORT': '3306',
        'TEST': {
            'CHARSET': 'utf8',      # 数据库的编码配置
            'COLLATION': 'utf8_general_ci',
        }
    }
}
二、 数据库设计

1. 一对一 关系
1 对 1 就是 1 个模型关联另一个模型,他们之间的关系是 1 对 1
一对一关联与多对一关联非常类似。若在模型中定义了 OneToOneField ,该模型的实例只需通过其属 性就能访问关联对象
class EntryDetail(models.Model):
	entry = models.OneToOneField(Entry, on_delete=models.CASCADE)
	details = models.TextField()
ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.不同点在于 “反向” 查询。一对一关联所关联的对象也能访问 Manager 对象,但这个 Manager 仅代表 一个对象,而不是对象的集合(QuerySet):
e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail object若未为关联关系指定对象,Django 会抛出 DoesNotExist 异常。
实例能通过为正向关联指定关联对象一样的方式指定给反向关联:
e.entrydetail = ed2. 模型代码设计
其中,request,step 和 config 参考 HR 对应部分的字段,由于其中会出现很多嵌套字段,所以 1 层的字段 就用 json 数据类型来代替
from django.db import models
class Config(models.Model):
    name = models.CharField('名称', max_length=128)
    # 可 null 可空白
    base_url = models.CharField('IP/域名', max_length=512, null=True, blank=True)
    variables = models.JSONField('变量', null=True)
    parameters = models.JSONField('参数', null=True)  # 用于参数化
    verify = models.BooleanField('https校验', default=False)
    export = models.JSONField('用例返回值', null=True)
    def __str__(self):
        return self.name
class Step(models.Model):
    # 同模型中,两个字段关联一个模型时,需要指定 related_name, 且名字不能相同
    # 属于哪个用例
    belong_case = models.ForeignKey('Case', on_delete=models.CASCADE, related_name='test_steps')
    # 引用那条用例
    linked_case = models.ForeignKey('Case', on_delete=models.SET_NULL, null=True, related_name='linked_steps')
    name = models.CharField('名称', max_length=128)
    variables = models.JSONField('变量', null=True)
    extract = models.JSONField('请求返回值', null=True)
    validate = models.JSONField('校验项', null=True)
    setup_hooks = models.JSONField('初始化', null=True)
    teardown_hooks = models.JSONField('清除', null=True)
    def __str__(self):
        return self.name
class Request(models.Model):
    method_choices = (  # method 可选字段, 二维元组
        (0, 'GET'),  # 参数 1 : 保存在数据库中的值 ,参数 2 : 对位显示的值
        (1, 'POST'),
        (2, 'PUT'),
        (3, 'DELETE'),
    )
    step = models.OneToOneField(Step, on_delete=models.CASCADE, null=True, , related_name='testrequest')
    method = models.SmallIntegerField('请求方法', choices=method_choices, default=0)
    url = models.CharField('请求路径', default='/', max_length=1000)
    params = models.JSONField('url参数', null=True)
    headers = models.JSONField('请求头', null=True)
    cookies = models.JSONField('cookies', null=True)
    data = models.JSONField('data参数', null=True)
    json = models.JSONField('json参数', null=True)
    def __str__(self):
        return self.url
class Case(models.Model):
    config = models.OneToOneField(Config, on_delete=models.DO_NOTHING)
    suite = models.ForeignKey('Suite', on_delete=models.DO_NOTHING, null=True)
    file_path = models.CharField('用例文件路径', max_length=1000, default='demo_case.json')
    def __str__(self):
        return self.config.name
class Suite(models.Model):
    config = models.OneToOneField(Config, on_delete=models.DO_NOTHING)
    file_path = models.CharField('套件文件路径', max_length=1000, default='demo_suite.json')
    def __str__(self):
        return self.config.name3. 同步数据库
生成 数据库 同步 文件
python manage.py makemigrations
同步数据库
python manage.py migrate
三、 反向查询概念解析
1. 正向查询
模型查询其关联的项目叫做正向查询(外键定义在模型)
例如:步骤查询其所在的用例
# test.py
class TestRelatedQuery(TestCase):
    def setUp(self) -> None:
        # 创建套件和用例
        self.config = Config.objects.create(name='用例1')
        self.case = Case.objects.create(config=self.config)
        self.config_suite = Config.objects.create(name='套件1')
        self.suite = Suite.objects.create(config=self.config_suite)
    def test_case_suite(self):
        # 用例关联套件
        self.case.suite = self.suite
        # 保存数据库
        self.case.save()
        print(self.case.suite)      # 正向查询-- 查询所属套件
        print(self.case.config)     # 正向查询 -- 查询配置2. 反向查询
反过来,项目查询下面的模型叫做反向查询,通过反向查询的结果 QuerySet
- 未指定 related_name 时 - 多对多 或 多对一 - modelobj.field_set.all()
- 一对一 - modelobj.field
 
class TestRelatedQuery(TestCase):
    def setUp(self) -> None:
        # 创建套件和用例
        self.config = Config.objects.create(name='用例1')
        self.case = Case.objects.create(config=self.config)
        self.config_suite = Config.objects.create(name='套件1')
        self.suite = Suite.objects.create(config=self.config_suite)
    def test_case_suite(self):
        self.case.suite = self.suite	# 用例关联套件
        self.case.save()	# 保存数据库
        # 反向查询 --- 多对一 或 多对多
        # 套件查询关联的用例 --- 数据对象.多方属性_set.all() --- 返回的是 QuerySet(查询集)
        print(self.suite.case_set.all())
        # 反向查询 --- 一对一
        # 配置对应的用例 --- 数据对象.反向关系模型小写
        print(self.config.case)方式 2:指定 related_name 时
modelobj.related_name.all()
class TestRelatedQuery(TestCase):
    def setUp(self) -> None:
        # 创建套件和用例
        self.config = Config.objects.create(name='用例1')
        self.case = Case.objects.create(config=self.config)
        self.config_suite = Config.objects.create(name='套件1')
        self.suite = Suite.objects.create(config=self.config_suite)
    def test_step_case(self):
        self.step = Step.objects.create(belong_case=self.case, name='步骤1')
        Step.objects.create(belong_case=self.case, name='步骤2')
        Step.objects.create(belong_case=self.case, name='步骤3')
        # 多对多 反向查询
        print(self.case.test_steps.all())
    def test_step_request(self):
        self.step = Step.objects.create(belong_case=self.case, name='步骤1')
        self.request = Request.objects.create(url='/demo/example', step=self.step)
        # 一对一 关系反向查询
        print(self.step.testrequest)四、 json 字段操作
1、 增
class TestJsonOperate(TestCase):
    def test_add(self):
        req1 = Request.objects.create(url='/demo1', data={'name': '小明', 'age': '18', 'addr': '新世纪大道'})
        print(req1.data)
        req2 = Request.objects.create(url='/demo1', params={'name': '小明', 'age': '18', 'addr': '新世纪大道'})
        print(req2.params)2. 改
def test_update(self):
    req1 = Request.objects.create(url='/demo1', data={'name': '小明', 'age': '18', 'addr': '新世纪大道'})
    # 整体修改
    req1.data = {'city': 'beijing'}
    req1.save()
    print(req1.data)
    # 局部修改
    req1.data['name'] = '小红'
    req1.save()
    print(req1.data)3. 删
def test_delete(self):
    req1 = Request.objects.create(url='/demo1', data={'name': '小明', 'age': '18', 'addr': '新世纪大道'})
    req1.data = None       # sql 的 none
    req1.save()
    print(req1.data)
    # 查询的时候关系 字段 等于 sql 的 null
    res = Request.objects.filter(data__isnull=True)
    print(res)
    req1.data = Value('null')       # json 的 none
    req1.save()
    # 查询的关系 字段 等于 json 的 null
    res = Request.objects.filter(data=None)
    print(res)4. 查
def test_query(self):
    req1 = Request.objects.create(url='/demo1', data={'name': '小明', 'age': '18', 'addr': '新世纪大道','father':{'age':40}})
    res = Request.objects.filter(data__father__age=40)
    print(res)