模型关联关系
用例和项目之间的关联可以直接通过config,因为config是套件和用例的扩展,相当于用例和套件本身 。
一、 模块的包管理
1. 创建模型包
如果模型过多时,需要把模型分别存储。
在应用中创建一个名为 models 的包
,将所有的模型文件全部存放在 该文件夹下, 删除原来的 models.py
文件。
2. 创建 模型文件
在 __init__.py
文件中,要使用 显示明确的方式导入每个模型,这样不会让命名混淆,便于可读。
1 2 3 4 5 6
|
from .hr3 import Request, Case, Config, Suite, Step from .auth import User from .base import CommonInfo from .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 models from .base import CommonInfo from 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 models from django.conf import settings
class 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 models
class 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 models
class 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 models from .base import CommonInfo from .mgr import Project
class 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 models from django.conf import settings
class 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 AbstractUser from django.db import models
class 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 models from .base import CommonInfo from 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 models from django.conf import settings
class 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), ]
|