测试平台——项目模块模型类设计

这里写目录标题

一、项目应用

1、项目包含接口:

  • 项目管理接口
  • 接口管理接口
  • 环境管理接口

2、创建子应用

python 复制代码
python manage.py startapp projects

3、项目模块设计

a、模型类设计

python 复制代码
from django.db import models

# Create your models here.
class Project(models.Model):
    """项目表"""
    name = models.CharField(max_length=50, help_text='项目名称', verbose_name='项目名')
    leader = models.CharField(max_length=50, help_text='负责人', verbose_name='负责人', default='')
    create_time = models.DateTimeField(verbose_name='创建时间', help_text='创建时间', auto_now_add=True)

    class Meta:
        db_table = 'tb_project'
        verbose_name = '项目表'
        verbose_name_plural = verbose_name

    def _str__(self):
        return self.name

b、序列化器类设计

python 复制代码
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer

class ProjectSerializer(ModelSerializer):
    class Meta:
        model = Project
        fields = ['id', 'name', 'leader', 'create_time']

c、视图类设计

python 复制代码
class ProjectViewSet(ModelViewSet):
    queryset = Project.objects.all()
    serializer_class = ProjectSerializer

获取项目的详情数据

需要展示字段:

【基本信息】执行环境、执行场景、测试计划、接口数量、定时任务、执行记录

【bug统计】未处理bug、处理中bug、处理完bug、无效bug

【执行记录】执行时间、执行人、执行环境、测试计划、总用例、通过数、测试报告

方法一:需要重写retrieve方法

python 复制代码
class ProjectViewSet(ModelViewSet):
    queryset = Project.objects.all()
    serializer_class = ProjectSerializer

    # permission_classes = [IsAuthenticated]

    def retrieve(self, request, *args, **kwargs):
        response = super().retrieve(self, request, *args, **kwargs)
        obj = self.get_object()
        response.data['info'] = [
            {'name': '执行环境', 'value': TestEnv.objects.filter(project_id=obj.id).count()},
            {'name': '执行场景', 'value': TestScene.objects.filter(project_id=obj.id).count()},
            {'name': '测试计划', 'value': TestPlan.objects.filter(project_id=obj.id).count()},
            {'name': '接口数量', 'value': Interface.objects.filter(project_id=obj.id).count()},
            {'name': '定时任务', 'value': 0},  # todo 定时任务未开发
            {'name': '执行记录', 'value': Record.objects.filter(plan__project=obj).count()}
        ]
        return response

方法二:模型类中定义新的字段(重点)

模型类

python 复制代码
from django.db import models

# Create your models here.
from reports.models import Record


class Project(models.Model):
    """项目表"""
    name = models.CharField(max_length=50, unique=True, help_text='项目名称', verbose_name='项目名')
    leader = models.CharField(max_length=50, help_text='负责人', verbose_name='负责人', default='')
    create_time = models.DateTimeField(verbose_name='创建时间', help_text='创建时间', auto_now_add=True)

    class Meta:
        db_table = 'tb_project'
        verbose_name = '项目表'
        verbose_name_plural = verbose_name

    def _str__(self):
        return self.name

    # todo 重要
    def info(self):
        return [
            {'name': '执行环境', 'value': self.test_envs.count()},
            {'name': '执行场景', 'value': self.test_scenes.count()},
            {'name': '测试计划', 'value': self.test_plans.count()},
            {'name': '接口数量', 'value': self.interfaces.count()},
            {'name': '定时任务', 'value': 0},       #todo 定时任务未开发
            {'name': '执行记录', 'value': Record.objects.filter(plan__project=self).count()}
        ]

    def bugs(self):
        return [
            {"name":"未处理bug",
             "value":self.bug_set.filter(status='未处理').count()},
            {"name": "处理中bug",
             "value": self.bug_set.filter(status='处理中').count()},
            {"name": "处理完bug",
             "value": self.bug_set.filter(status='处理完').count()},
            {"name": "无效bug",
             "value": self.bug_set.filter(status='无效bug').count()},
        ]

序列化器设计

python 复制代码
class ProjectSerializer(ModelSerializer):
    class Meta:
        model = Project
        fields = ['id', 'name', 'leader', 'create_time', 'info', 'bugs']

视图设计

python 复制代码
class ProjectViewSet(ModelViewSet):
    queryset = Project.objects.all()
    serializer_class = ProjectSerializer

d、项目的增删改查操作

新增项目(http://127.0.0.1:8000/projects/)

python 复制代码
{
    "id": 21,
    "name": "872344567",
    "leader": "65325346",
    "create_time": "2023-08-06T12:18:26.752695+08:00",
    "info": [
        {
            "name": "执行环境",
            "value": 0
        },
        {
            "name": "执行场景",
            "value": 0
        },
        {
            "name": "测试计划",
            "value": 0
        },
        {
            "name": "接口数量",
            "value": 0
        },
        {
            "name": "定时任务",
            "value": 0
        },
        {
            "name": "执行记录",
            "value": 0
        }
    ],
    "bugs": [
        {
            "name": "未处理bug",
            "value": 0
        },
        {
            "name": "处理中bug",
            "value": 0
        },
        {
            "name": "处理完bug",
            "value": 0
        },
        {
            "name": "无效bug",
            "value": 0
        }
    ]
}

更新项目(http://127.0.0.1:8000/projects/18/)

删除项目(http://127.0.0.1:8000/projects/17/)

项目的详情信息(http://127.0.0.1:8000/projects/1/)

python 复制代码
{
    "id": 1,
    "name": "20230730",
    "leader": "巴特勒",
    "create_time": "2023-07-30T21:33:50.016376+08:00",
    "info": [
        {
            "name": "执行环境",
            "value": 2
        },
        {
            "name": "执行场景",
            "value": 3
        },
        {
            "name": "测试计划",
            "value": 2
        },
        {
            "name": "接口数量",
            "value": 5
        },
        {
            "name": "定时任务",
            "value": 0
        },
        {
            "name": "执行记录",
            "value": 30
        }
    ],
    "bugs": [
        {
            "name": "未处理bug",
            "value": 2
        },
        {
            "name": "处理中bug",
            "value": 1
        },
        {
            "name": "处理完bug",
            "value": 1
        },
        {
            "name": "无效bug",
            "value": 1
        }
    ]
}

疑惑点:为什么extra_kwargs中定义的字段不生效?
extra_kwargs、read_only_fields等字段只对模型类中已经存在的字段生效

python 复制代码
class ProjectSerializer(ModelSerializer):
    class Meta:
        model = Project
        fields = ['id', 'name', 'leader', 'create_time', 'info', 'bugs']
        # fields = ['id', 'name', 'leader', 'create_time']
        extra_kwargs={
            "info":{
                "read_only":True
            },
            "bugs": {
                "read_only": True
            },
        }

4、接口模块设计

a、模型类设计

python 复制代码
class Interface(models.Model):
    """接口表"""
    CHOICES = [
        ('1', '项目接口'),
        ('2', '外部接口')]

    project = models.ForeignKey('Project',
                                on_delete=models.CASCADE,
                                help_text='项目id',
                                verbose_name='项目id',
                                related_name='interfaces')
    name = models.CharField(max_length=50, help_text='接口名称', verbose_name='接口名称')
    url = models.CharField(max_length=200, help_text='接口路径', verbose_name='接口路径')
    method = models.CharField(max_length=50, help_text='请求方法', verbose_name='请求方法')
    type = models.CharField(verbose_name='接口类型',
                            help_text='接口类型',
                            max_length=40,
                            choices=CHOICES,
                            default='1')

    # 扩展,接口的说明,接口字段的参数

    def __str__(self):
        return self.url

    class Meta:
        db_table = 'tb_interface'
        verbose_name = '接口表'
        verbose_name_plural = verbose_name

b、序列化器类设计

python 复制代码
class NestTestStepSerializer(ModelSerializer):
    """嵌套测试步骤序列化器"""

    class Meta:
        model = TestStep
        fields = ['id', 'title']


class InterfaceSerializer(ModelSerializer):
    steps = NestTestStepSerializer(many=True, read_only=True, source='teststep_set')

    class Meta:
        model = Interface
        fields = '__all__'

    def validate(self, attrs):
        url = attrs.get('url')
        type_ = attrs.get('type')
        if type_ == '2':
            if not url.startswith('http'):
                raise serializers.ValidationError('外部接口的url需要完整的url,必须以http或者https开头')
        return attrs

获取接口的列表信息时:还需要将接口的下用例信息获取出来
接口表与用例表的关系为一对多,关联关系字段为:interface。
用例数据获取所关联的接口数据用字段:interface
接口数据获取所关联的用例数据用字段:模型类名小写_set(teststep_set)
teststep_set = NestTestStepSerializer(many=True, read_only=True)。
当采用source时,又一种新的编写方法
source指定关联关系字段teststep_set;展示的字段可以随意定义
steps = NestTestStepSerializer(many=True, read_only=True, source='teststep_set')

c、视图类设计

python 复制代码
class InterfaceViewSet(ModelViewSet):
    queryset = Interface.objects.all()
    serializer_class = InterfaceSerializer
    filterset_fields = ['project', 'type']

d、接口的增删改查查操作

接口的增加

http://127.0.0.1:8000/interfaces/

接口的更新

http://127.0.0.1:8000/interfaces/7/

python 复制代码
{
    "id": 7,
    "steps": [],
    "name": "0806接口",
    "url": "/interfaces/",
    "method": "GET",
    "type": "1",
    "project": 1
}

接口的删除

http://127.0.0.1:8000/interfaces/9/

接口的查询

http://127.0.0.1:8000/interfaces/?project=1
过滤字段格式:?字段名=字段值
字段名必须是当前模型类中存在的字段
filterset_fields = ['project', 'type']

python 复制代码
[
    {
        "id": 1,
        "steps": [
            {
                "id": 1,
                "title": "登录成功"
            },
            {
                "id": 8,
                "title": "登录成功1"
            }
        ],
        "name": "登录",
        "url": "/users/login/",
        "method": "POST",
        "type": "1",
        "project": 1
    },
    {
        "id": 2,
        "steps": [],
        "name": "wai1",
        "url": "https://www.baidu.com",
        "method": "POST",
        "type": "2",
        "project": 1
    },
    {
        "id": 3,
        "steps": [
            {
                "id": 3,
                "title": "查看项目列表信息"
            }
        ],
        "name": "查看项目列表",
        "url": "/projects/",
        "method": "GET",
        "type": "1",
        "project": 1
    },
    {
        "id": 5,
        "steps": [
            {
                "id": 7,
                "title": "注册-正向用例"
            }
        ],
        "name": "注册",
        "url": "/users/register/",
        "method": "POST",
        "type": "1",
        "project": 1
    },
    {
        "id": 6,
        "steps": [
            {
                "id": 9,
                "title": "创建项目正向用例"
            }
        ],
        "name": "创建项目",
        "url": "/projects/",
        "method": "POST",
        "type": "1",
        "project": 1
    }
]

5、环境模块设计

a、模型类设计

python 复制代码
class TestEnv(models.Model):
    """测试坏境表"""
    name = models.CharField(max_length=150, help_text='环境名称', verbose_name='环境名称')
    project = models.ForeignKey(Project,
                                on_delete=models.CASCADE,
                                help_text='项目id',
                                verbose_name='项目id')
    global_variable = models.JSONField(help_text='全局变量',
                                       verbose_name='全局变量',
                                       default=dict,
                                       null=True)
    debug_global_variable = models.JSONField(help_text='debug模式全局变量',
                                             verbose_name="debug模式全局变量",
                                             default=dict,
                                             null=True)
    db = models.JSONField(help_text='数据库配置',
                          verbose_name='数据库配置',
                          default=list,
                          null=True,blank=True)
    host = models.CharField(help_text='base_url地址',
                            verbose_name='base_url地址',
                            max_length=100,
                            null=True,blank=True)
    headers = models.JSONField(help_text='请求头',
                               verbose_name='请求头',
                               default=dict,
                               null=True, blank=True)
    global_func = models.TextField(help_text='全局工具函数',
                                   verbose_name='全局工具函数',
                                   default=open('./utils/global_func.py', 'r',
                                                encoding='utf-8').read(),
                                   null=True,blank=True)

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'tb_testenv'
        verbose_name = '接口表'
        verbose_name_plural = verbose_name

b、序列化器类设计

python 复制代码
class EnvSerializer(serializers.ModelSerializer):
    class Meta:
        model = TestEnv
        fields = '__all__'

c、视图类设计

python 复制代码
class EnvViewSet(ModelViewSet):
    queryset = TestEnv.objects.all()
    serializer_class = EnvSerializer
    filterset_fields = ['project']

d、环境的增删改查操作

环境的增加

http://127.0.0.1:8000/test_envs/

python 复制代码
{
    "id": 4,
    "name": "New Env",
    "global_variable": {},
    "debug_global_variable": {},
    "db": [],
    "host": null,
    "headers": {},
    "global_func": "\"\"\"\n自定义全局工具函数\n============================\n\"\"\"\nfrom apitestengine.core.tools import *\n",
    "project": 1
}

环境的更新

http://127.0.0.1:8000/test_envs/3/

python 复制代码
{
    "id": 3,
    "name": "项目1环境",
    "global_variable": {
        "a": 1
    },
    "debug_global_variable": {},
    "db": [],
    "host": "127.0.0.1:8000",
    "headers": {
        "authorization": "application/json"
    },
    "global_func": "\"\"\"\n自定义全局工具函数\n============================\n\"\"\"\nfrom apitestengine.core.tools import *\n\ndef test_name():\n    return '666'",
    "project": 1
}

环境的删除

http://127.0.0.1:8000/test_envs/2/

环境的查询

http://127.0.0.1:8000/test_envs/?project=1
过滤字段格式:?字段名=字段值
字段名必须是当前模型类中存在的字段
filterset_fields = ['project']

python 复制代码
[
    {
        "id": 1,
        "name": "env1",
        "global_variable": {},
        "debug_global_variable": {
            "host": "http://127.0.0.1:8000",
            "headers": {},
            "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImppbWkiLCJleHAiOjE2OTEzMjkwOTAsImVtYWlsIjoiMTFAcXEuY29tIn0.bm_b2Mj85JBvkAx0Ao02AFzFpVBizraZaC-_vC25IOA",
            "username": "王帅"
        },
        "db": [
            {
                "name": "db1",
                "type": "mysql",
                "config": {
                    "user": "root",
                    "password": "root",
                    "host": "82.156.178.247",
                    "port": 8848,
                    "autocommit": true,
                    "database": "ck14_database"
                }
            }
        ],
        "host": "http://127.0.0.1:8000",
        "headers": {},
        "global_func": "\"\"\"\n自定义全局工具函数\n\n\"\"\"\n\nfrom apitestengine.core.tools import *\n\n\n# def random_test_name():\n#     return 'test_'+fk.pystr()\n\ndef random_date():\n    \"\"\"随机生成一个日期\"\"\"\n    return fk.date()\nprint(random_date())\n\n# print('random_test_name的值为:',random_test_name())\n\n# from faker import Faker\n# fk = Faker(locale='zh_CN')\n\ndef test_username():\n    return fk.name()\n\n\n# def random_city()\n#     return fk.city()\n    \n# print(random_city())",
        "project": 1
    },
    {
        "id": 3,
        "name": "项目1环境",
        "global_variable": {
            "a": 1
        },
        "debug_global_variable": {},
        "db": [],
        "host": "127.0.0.1:8000",
        "headers": {
            "authorization": "application/json"
        },
        "global_func": "\"\"\"\n自定义全局工具函数\n============================\n\"\"\"\nfrom apitestengine.core.tools import *\n\ndef test_name():\n    return '666'",
        "project": 1
    },
    {
        "id": 4,
        "name": "New Env",
        "global_variable": {},
        "debug_global_variable": {},
        "db": [],
        "host": null,
        "headers": {},
        "global_func": "\"\"\"\n自定义全局工具函数\n============================\n\"\"\"\nfrom apitestengine.core.tools import *\n",
        "project": 1
    }
]

6、DRF中的通用过滤

除了能够覆盖默认查询集之外,REST 框架还包括对通用过滤后端的支持,允许您轻松构建复杂的搜索和过滤器。

6.1、设置过滤器后端

可以使用设置全局设置默认过滤器后端DEFAULT_FILTER_BACKENDS。例如。

a、要使用DjangoFilterBackend,请先安装django-filter

python 复制代码
pip install django-filter

注意djoango-filter仅支持:

● Python: 3.6,3.7,3.8

● Django: 2.2, 3.1,3.2

● DRF: 3.10+

b、注册

python 复制代码
INSTALLED_APPS = [
    ...
    'django_filters',
	...
]

c、配置

python 复制代码
REST_FRAMEWORK = {
	...
	...
    # 过滤
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
}

d、使用
在后端类视图或者视图集中添加filterset_fields属性即可实现指定字段过滤,例如改写环境管理视图集

如下:

python 复制代码
class EnvViewSet(ModelViewSet):
    queryset = TestEnv.objects.all()
    serializer_class = EnvSerializer
    filterset_fields = ['name','project']

不需要在复写get_queryset方法手动过滤,就可以通过url/test_envs/?project=1访问过滤数据了。
注意:当使用外键字段过滤时,如果级联模式是删除,则使用不存在的数据过滤会返回400响应。例如上面的
视图如果使用不存在的项目id过滤返回结果如下:

python 复制代码
http://127.0.0.1:8000/test_envs?project=2
相关推荐
Q_19284999065 分钟前
基于Spring Boot的摄影器材租赁回收系统
java·spring boot·后端
良许Linux10 分钟前
0.96寸OLED显示屏详解
linux·服务器·后端·互联网
求知若饥22 分钟前
NestJS 项目实战-权限管理系统开发(六)
后端·node.js·nestjs
qq_5290252924 分钟前
Torch.gather
python·深度学习·机器学习
数据小爬虫@25 分钟前
如何高效利用Python爬虫按关键字搜索苏宁商品
开发语言·爬虫·python
Cachel wood1 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
終不似少年遊*1 小时前
pyecharts
python·信息可视化·数据分析·学习笔记·pyecharts·使用技巧
gb42152871 小时前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端
程序猿进阶1 小时前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
Python之栈1 小时前
【无标题】
数据库·python·mysql