模型关联关系
用例和项目之间的关联可以直接通过 config,因为 config 是套件和用例的扩展,相当于用例和套件本身 。
一、 模块的包管理 1. 创建模型包 如果模型过多时,需要把模型分别存储。
在应用中创建一个名为 models 的包
,将所有的模型文件全部存放在 该文件夹下, 删除原来的 models.py
文件。
2. 创建 模型文件 在 __init__.py
文件中,要使用 显示明确的方式导入每个模型,这样不会让命名混淆,便于可读。
1 2 3 4 5 6 from .hr3 import Request, Case, Config, Suite, Stepfrom .auth import Userfrom .base import CommonInfofrom .mgr import Environment, Project
hr3.py 存放所有核心数据模型
mgr.py 存放所有项目管理相关模型
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 from django.db import modelsfrom .base import CommonInfofrom django.conf import settings class Project (CommonInfo ): PRO_STATUS = ( (0 , '开发中' ), (1 , '维护中' ), (2 , '稳定运行' ), ) admin = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, null=True , related_name='admin_pros' , verbose_name='管理员' ) members = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='member_pros' , verbose_name='成员' ) name = models.CharField('项目名称' , max_length=256 , unique=True ) status = models.SmallIntegerField('项目状态' , choices=PRO_STATUS, default=0 ) version = models.CharField('项目版本' , default='v0.1' , max_length=10 ) class Meta (CommonInfo.Meta): verbose_name = '项目表' class Environment (CommonInfo ): service_type = ( (0 , 'web服务器' ), (1 , '数据库服务器' ), (2 , '缓存服务器' ), ) service_os = ( (0 , 'windows' ), (1 , 'linux' ), ) service_status = ( (0 , 'active' ), (1 , 'disable' ), ) project = models.ForeignKey(Project, on_delete=models.CASCADE, verbose_name='所属项目' ) ip = models.GenericIPAddressField('IP地址' , default='127.0.0.1' ) port = models.CharField('端口号' , max_length=5 , default='8080' ) category = models.SmallIntegerField('服务器类型' , choices=service_type, default=0 ) os = models.SmallIntegerField('操作系统' , choices=service_os, default=1 ) status = models.SmallIntegerField('服务器状态' , choices=service_status, default=0 ) class Meta (CommonInfo.Meta): verbose_name = '测试环境表'
二、 模型继承 1. 抽象类 通友字段
用一个 抽象模型类
来存放,然后其他模型继承该抽象模型类,例如:创建时间、更新时间等
在元类中使用 abstract = True
定义 抽象类
,抽象类 不会创建 数据库表
auto_now_add
: 第 1 次创建数据时自动添加当前时间
auto_now
:每次更新数据时自动添加当前时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from django.db import modelsfrom django.conf import settingsclass CommonInfo (models.Model): creator = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, null=True ,related_name='%(app_label)s_%(class)s_create' ) updater = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, null=True ,related_name='%(app_label)s_%(class)s_update' ) create_time = models.DateTimeField(auto_now_add=True , verbose_name='创建时间' ) update_time = models.DateTimeField(auto_now=True , verbose_name='更新时间' , null=True ) decs = models.CharField('描述' , max_length=1024 , null=True , blank=True ) class Meta : abstract = True ordering = ['id' ]
子类继承父类:
1 2 3 4 5 6 class Project (CommonInfo ): ... class Environment (CommonInfo ): ...
如果子类没有声明自己的 Meta 类,那么它将自动继承抽象基类的 Meta 类。
如果子类要设置自己的 Meta 属性,则需要扩展基类的 Meta:
1 2 3 4 5 6 7 8 9 10 11 12 from django.db import modelsclass CommonInfo (models.Model): class Meta : abstract = True ordering = ['name' ] class Student (CommonInfo ): class Meta (CommonInfo.Meta): db_table = 'student_info'
这里有几点要特别说明:
抽象基类中有元数据,子模型没有的话,直接继承; 抽象基类中有元数据,子模型也有的话,直接覆盖; 子模型可以额外添加元数据; 抽象基类中的 abstract=True 这个元数据不会被继承。也就是说如果想让一个抽象基类的子模型,同样成为一个抽象基类,那你必须显式的在该子模型的 Meta 中同样声明一个 abstract =True ; 有一些元数据对抽象基类无效,比如 db_table ,首先是抽象基类本身不会创建数据表,其次它的所有子类也不会按照这个元数据来设置表名。 由于 Python 继承的工作机制,如果子类继承了多个抽象基类,则默认情况下仅继承第一个列出的基 类的 Meta 选项。如果要从多个抽象基类中继承 Meta 选项,必须显式地声明 Meta 继承。例如: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from django.db import modelsclass CommonInfo (models.Model): name = models.CharField(max_length=100 ) age = models.PositiveIntegerField() class Meta : abstract = True ordering = ['name' ] class Unmanaged (models.Model): class Meta : abstract = True managed = False class Student (CommonInfo, Unmanaged): home_group = models.CharField(max_length=5 ) class Meta (CommonInfo.Meta, Unmanaged.Meta): pass
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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 from django.db import modelsfrom .base import CommonInfofrom .mgr import Projectclass Config (models.Model): project = models.ForeignKey(Project, on_delete=models.DO_NOTHING, null=True , verbose_name='所属项目' ) name = models.CharField('名称' , max_length=128 ) 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 Meta : ordering = ['id' ] class Step (models.Model): 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 Meta : ordering = ['id' ] class Request (CommonInfo ): method_choices = ( (0 , 'GET' ), (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 Meta (CommonInfo.Meta): ordering = ['id' ] verbose_name = '接口请求表' class Case (CommonInfo ): 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 Meta (CommonInfo.Meta): ordering = ['id' ] verbose_name = '用例表' class Suite (CommonInfo ): 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 class Meta (CommonInfo.Meta): ordering = ['id' ] verbose_name = '套件表'
如果 抽象类中 存在 ForeignKey
或者 ManyToManyField
字段,那么使用 related_name
或者 related_query_name
参数的值就不能固定,否则在正向查询或反向查询时会出现错误。
解决办法:
related_name
或者 related_query_name
的值中包含 %(app_label)s
和 %(class)s
部分
%(class)s
: 用字段所属子类的小写名替换%(app_label)s
: 用子类所属 app 的小写名替换1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from django.db import modelsfrom django.conf import settingsclass CommonInfo (models.Model): creator = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, null=True ,related_name='%(app_label)s_%(class)s_create' ) updater = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, null=True ,related_name='%(app_label)s_%(class)s_update' ) create_time = models.DateTimeField(auto_now_add=True , verbose_name='创建时间' ) update_time = models.DateTimeField(auto_now=True , verbose_name='更新时间' , null=True ) decs = models.CharField('描述' , max_length=1024 , null=True , blank=True ) class Meta : abstract = True ordering = ['id' ]
common.ChildA.m2m 字段的 reverse name (反向关系名)应该是 common_childa_related ;reverse query name (反向查询名)应该是 common_childas 。 common.ChildB.m2m 字段的反向关系名应该是 common_childb_related ;反向查询名应该是 common_childbs 。 rare.ChildB.m2m 字段的反向关系名应该是 rare_childb_related ;反向查询名应该是 rare_childbs 。 三、 用户管理 1. 自定义用户模型 当我们执行 migrate 创建数据库表时,就会为我们创建 用户表 auth_user ,如下所示
如果模型中的字段不符合我们的需求时,通过继承 django.contrib.auth.models
里面的 AbstractUser
类的方式,在你项目文件的 models
里面进行如下定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from django.contrib.auth.models import AbstractUserfrom django.db import modelsclass User (AbstractUser ): USER_TYPE = ( (0 , '开发' ), (1 , '测试' ), (2 , '运维' ), (3 , '项目经理' ), ) realname = models.CharField('姓名' , max_length=32 , null=True ) phone = models.CharField('手机号' , max_length=11 , unique=True ) user_type = models.SmallIntegerField('用户类型' , choices=USER_TYPE, default=1 )
这样就在原来的 contrib.auth 里面的 user 表的基础上,新增了 usertype
、realname
、phone
、 这些字段
在 settings.py
文件中 设置,使 Django 以该表作为 user 表
1 2 3 4 AUTH_USER_MODEL = 'sqpt.User'
同步数据库模型前,需要:
1.删除 migrations 目录及所有文件,
2.删除数据库并重写创建。
3.再执行数据库同步。
2. 引用 User 模型 直接引用 User 模型,那么以后我们的项目改成不同的用户模型时,就无法自动切到 AUTH_USER_MODEL 配置的用户模型 ,因此应该直接引用 AUTH_USER_MODEL
配置的用户模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from django.db import modelsfrom .base import CommonInfofrom django.conf import settings class Project (CommonInfo ): PRO_STATUS = ( (0 , 'developing' ), (1 , 'operating' ), (2 , 'stable' ), ) admin = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, null=True , related_name='admin_pros' , verbose_name='%(app_label)s_%(class)s_admin' ) members = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='member_pros' ,verbose_name='%(app_label)s_%(class)s_members' ) name = models.CharField('项目名称' , max_length=256 , unique=True ) status = models.SmallIntegerField('项目状态' , choices=PRO_STATUS, default=0 ) version = models.CharField('项目版本' , default='v0.1' , max_length=10 ) class Meta (CommonInfo.Meta): verbose_name = '项目表'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from django.db import modelsfrom django.conf import settingsclass CommonInfo (models.Model): creator = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, null=True , verbose_name='创建者' , related_name='%(app_label)s_%(class)s_create' ) updater = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, null=True , verbose_name='更新者' , related_name='%(app_label)s_%(class)s_update' ) create_time = models.DateTimeField(auto_now_add=True , verbose_name='创建时间' ) update_time = models.DateTimeField(auto_now=True , verbose_name='更新时间' , null=True ) decs = models.CharField('描述' , max_length=1024 , null=True , blank=True ) class Meta : abstract = True ordering = ['id' ]
同步数据库:
1 2 python manage.py makemigrations sqpt python manage.py migrate
四、 视图开发 1. 创建序列化器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class ProjectSerializer (serializers.ModelSerializer): class Meta : model = Project fields = '__all__' class EnvironmentSerializer (serializers.ModelSerializer): class Meta : model = Environment fields = '__all__' class UserSerializer (serializers.ModelSerializer): class Meta : model = User fields = '__all__'
2. 创建视图 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 class ProjectViewSet (viewsets.ModelViewSet): queryset = Project.objects.all () serializer_class = ProjectSerializer class EnvironmentViewSet (viewsets.ModelViewSet): queryset = Environment.objects.all () serializer_class = EnvironmentSerializer @api_view(['GET' ] ) def user_lest (request ): query_set = User.objects.all () serializer = UserSerializer(query_set, many=True ) return Response(serializer.data) @api_view(['GET' ] ) def user_detail (request, _id ): try : user_obj = User.objects.get(pk=_id ) except User.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) serializer = UserSerializer(user_obj) return Response(serializer.data)
3. 创建路由 1 2 3 4 5 6 7 8 urlpatterns = [ path('' , include(router.urls)), path('users/' , views.user_lest), path('users/<int:_id>' , views.user_detail), ]