测试平台开发 - 平台数据模型设计

一、 数据库配置

1. 搭建 MySQL 数据库

数据库搭建(🚀CentOS 8 搭建 MySQL 8)

2. 创建数据库

1
CREATE DATABASE `course_autotp` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

3. 安装 pymysqlclient 模块

1
2
3
# 链接 mysql 数据库需要此模块,此模块安装不了的,参考附录的方法

pip install mysqlclient

4. 配置 数据库参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 ,该模型的实例只需通过其属
性就能访问关联对象

1
2
3
4
5
6
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):

1
2
e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail object

若未为关联关系指定对象,Django 会抛出 DoesNotExist 异常。

实例能通过为正向关联指定关联对象一样的方式指定给反向关联:

1
e.entrydetail = ed

2. 模型代码设计

其中,request,step和config参考HR对应部分的字段,由于其中会出现很多嵌套字段,所以1层的字段
就用json数据类型来代替

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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.name

3. 同步数据库

生成 数据库 同步 文件

1
python manage.py makemigrations

同步数据库

1
python manage.py migrate

三、 反向查询概念解析

1. 正向查询

模型查询其关联的项目叫做正向查询(外键定义在模型)

例如:步骤查询其所在的用例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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、 增

1
2
3
4
5
6
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. 改

1
2
3
4
5
6
7
8
9
10
11
12
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. 删

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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. 查

1
2
3
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)