九、REST framework接口开发:基于APIView接口实现

1、新建数据表<项目开发很少使用ForeignKey>,本项目也不使用,只使用M2M

# 接口请求参数表
class Interface_request_data(models.Model):
    db_table = 'interface_request_data'
    id = models.AutoField(max_length=200, verbose_name='主键', primary_key=True, unique=True)
    interface_name = models.CharField(max_length=50, verbose_name='接口名称')
    request_url = models.CharField(max_length=200, verbose_name='接口请求地址', unique=True)
    request_path = models.CharField(max_length=200, verbose_name='接口请求路径')
    is_run = models.CharField(max_length=20, verbose_name='接口是否运行')
    request_method = models.CharField(max_length=20, verbose_name='接口请求类型')
    request_headers = models.JSONField(default=dict, blank=True, verbose_name='json格式的接口请求头')
    create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    case_data = models.ManyToManyField(verbose_name="标签", to="Interface_case_data")

    def __str__(self):
        return self.interface_name

# 接口请求用例表
class Interface_case_data(models.Model):
    db_table = 'interface_case_data'
    id = models.AutoField(max_length=200, verbose_name='主键', primary_key=True, unique=True)
    parent_id = models.IntegerField(max_length=200, verbose_name='父类id', unique=True)
    interface_case_name = models.CharField(max_length=50, verbose_name='接口用例名称')
    request_data = models.CharField(max_length=200, verbose_name='接口请求参数')
    request_expected_results = models.CharField(max_length=200, verbose_name='接口预期结果', unique=True)
    create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")

    def __str__(self):
        return self.interface_case_name

2、接口开发,增删改查查,查查为单条数据查询和全部查询

# 创建序Interface_case_data列化器类
class CaseDataSerializer(serializers.ModelSerializer):

    class Meta:
        model = Interface_case_data
        fields = ["id", "interface_case_name"]


# 创建Interface_request_data序列化器类
class InterfaceDataSerializer(serializers.ModelSerializer):
    # required=False非必填字段,如post请求时,可以携带此字段也可以不携带
    # read_only=True表示只读字段,当为True时,字段数据只读不会写入数据库
    # case_data为get请求时,返回字段数据,必须携带,当为post请求时,也可以使用,为区分两者关系,添加case_id用于维护M2M
    # case_data 基于ManyToManyField使用嵌套的形式,嵌套使用连接:
    # https://www.cnblogs.com/guanyf/p/18585619
    case_data = CaseDataSerializer(many=True, required=False, read_only=True)
    # 表示只接受 List形式的数据,结合钩子使用,required=False表示非必填项
    case_id = serializers.ListField(required=False)

    class Meta:
        model = Interface_request_data
        fields = ["interface_name", "request_url", "request_path", "case_data", "case_id"]

    # 字段钩子,转换为QuerySet对象
    def validate_case_id(self, value):
        # QuerySet 对象,当传递QuerySet对象时,序列化器字段帮助我们维护多对多的关系表
        case_date_db = Interface_case_data.objects.filter(id__in=value)
        return case_date_db


class RequestDataView(MyAPIView):
    # 认证类,没有时为空
    authentication_classes = []
    permission_classes = []

    # 新建get方法
    def get(self, request, *args, **kwargs):
        # 查询数据库数据
        queryset = Interface_request_data.objects.all()
        # 进行序列化
        ser = InterfaceDataSerializer(instance=queryset, many=True)
        # 返回序列化后的数据
        return Response(ser.data)

    # 新建post方法
    def post(self, request, *args, **kwargs):
        ser = InterfaceDataSerializer(data=request.data)
        # ser.is_valid(),校验字段方法,根据序列化器
        if ser.is_valid():
            try:
                # 注意,此处的case_id表示移除序列化数据集合中的数据,当case_id没有时,报错KeyError: 'case_id'
                # 报错时,抛出异常,不做任何处理。
                # 使用case_id目的:1、演示可以自定义字段并且可以通过钩子重新定义字段
                #                 2、因为DRF的默认序列化器并不直接支持通过嵌套序列化器来更新多对多关系,通过钩子可以维护
                case_queryset = ser.validated_data.pop('case_id')
                ser.validated_data['case_data'] = case_queryset
            except KeyError as e:
                pass
            ser.save()
            # ser.data为默认写法,数据从ser.data中获取,详细逻辑查看源码
            return Response(ser.data)
        else:
            # 当序列化错误时,错误信息字段写入ser.errors,如:字段过长等信息
            return Response(ser.errors)


class RequestDataDetailView(MyAPIView):

    # 认证类,没有时,注释掉
    authentication_classes = []

    # 单查询
    def get(self, request, *args, **kwargs):
        # 获取所有数据
        db_data = Interface_request_data.objects.filter(pk=kwargs["id"]).all()
        ser = InterfaceDataSerializer(instance=db_data, many=True)  # 序列化
        return Response(ser.data)  # 返回

    # 单条数据更新,演示m2m数据更新,当没有m2m时,手动处理逻辑
    def put(self, request, *args, **kwargs):
        # 获取单条数据
        db_date = Interface_request_data.objects.filter(id=kwargs["id"]).first()
        # 进行序列化
        ser = InterfaceDataSerializer(instance=db_date, data=request.data)
        if ser.is_valid():  # 验证字段,注意:验证字段根据data进行验证
            try:
                # 判断是否包含case_id字段,如果有就替换成case_data
                # ser.validated_data.pop('case_id')只配合is_valid使用
                # 通过字段钩子case_id的值,已经替换为queryset对象
                case_queryset = ser.validated_data.pop('case_id')
                # 因models中,只接受字段case_data,不接收case_id,需要替换key,方便m2m进行关联
                ser.validated_data['case_data'] = case_queryset
            except KeyError as e:
                pass
            # 触发save方法时,自动进行m2m字段抽取和组合,具体逻辑,请查看源码
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)

    def delete(self, request, *args, **kwargs):
        db_data = Interface_request_data.objects.filter(id=kwargs["id"]).first()
        # 当有关联时,自动删除联系
        db_data.delete()
        return Response()

 3、url配置

urlpatterns = [
    # <str:version> 表示捕获url该部分的信息,并赋值给version,并且这个变量在视图函数中可以通过version这个名称来访问
    # 当然在请求中可以获取到version这值,但是会导致后续很多复杂逻辑,不建议怎么做
    # URLPathVersioning会对version进行处理,可以避免其他逻辑性问题
    path('api/<str:version>/user/', views.RequestDataView.as_view()),
    re_path("api/(?P<version>.+)/user/(?P<id>\d+)", views.RequestDataDetailView.as_view()),
]

 4、通过源码可以知道,当save操作时,是调用了父类的update和create方法,那么我们可以重写update和create,降低代码的耦合性

class CaseDataSerializer(serializers.ModelSerializer):

    class Meta:
        model = Interface_case_data
        fields = ["id", "interface_case_name"]


# 创建Interface_request_data序列化器类
class InterfaceDataSerializer(serializers.ModelSerializer):
    # required=False非必填字段,如post请求时,可以携带此字段也可以不携带
    # read_only=True表示只读字段,当为True时,字段数据只读不会写入数据库
    # case_data为get请求时,返回字段数据,必须携带,当为post请求时,也可以使用,为区分两者关系,添加case_id用于维护M2M
    # case_data 基于ManyToManyField使用嵌套的形式,嵌套使用连接:
    # https://www.cnblogs.com/guanyf/p/18585619
    case_data = CaseDataSerializer(many=True, required=False, read_only=True)
    # 表示只接受 List形式的数据,结合钩子使用,required=False表示非必填项
    case_id = serializers.ListField(required=False)

    class Meta:
        model = Interface_request_data
        fields = ["interface_name", "request_url", "request_path", "case_data", "case_id"]

    # 字段钩子,转换为QuerySet对象
    def validate_case_id(self, value):
        # QuerySet 对象,当传递QuerySet对象时,序列化器字段帮助我们维护多对多的关系表
        case_date_db = Interface_case_data.objects.filter(id__in=value)
        return case_date_db

    # 重写父类的create方法
    def create(self, validated_data):
        example_relationship_data = None
        if 'case_id' in validated_data:
            # 从验证后的数据中剔除多对多字段,并拿到相关数据
            example_relationship_data = validated_data.pop('case_id')
        # 先提交没有m2m的数据
        instance = Interface_request_data.objects.create(**validated_data)
        # 检查是否有多对多字段数据需要更新
        if example_relationship_data is not None:
            # 如果有m2m数据,使用set方法进行更新m2m数据
            # case_data是 m2m 字段,在models中有规定,然后使用了 set() 方法来更新多对多关系
            instance.case_data.set(example_relationship_data)
        return instance

    # 重写父类的update方法
    def update(self, instance, validated_data):
        example_relationship_data = None
        # 从验证后的数据中剔除多对多字段,并拿到相关数据
        if 'case_id' in validated_data:
            # 从验证后的数据中剔除多对多字段,并拿到相关数据
            example_relationship_data = validated_data.pop('case_id')
        # 保存不包含多对多字段的模型实例
        instance.save()
        # 检查是否有多对多字段数据需要更新
        if example_relationship_data is not None:
            # case_data是 m2m 字段,在models中有规定,然后使用了 set() 方法来更新多对多关系
            instance.case_data.set(example_relationship_data)
        return instance


class RequestDataView(MyAPIView):
    # 认证类,没有时为空
    authentication_classes = []
    permission_classes = []

    # 新建get方法
    def get(self, request, *args, **kwargs):
        # 查询数据库数据
        queryset = Interface_request_data.objects.all()
        # 进行序列化
        ser = InterfaceDataSerializer(instance=queryset, many=True)
        # 返回序列化后的数据
        return Response(ser.data)

    # 新建post方法
    def post(self, request, *args, **kwargs):
        ser = InterfaceDataSerializer(data=request.data)
        # ser.is_valid(),校验字段方法,根据序列化器
        if ser.is_valid():
            ser.save()
            # ser.data为默认写法,数据从ser.data中获取,详细逻辑查看源码
            return Response(ser.data)
        else:
            # 当序列化错误时,错误信息字段写入ser.errors,如:字段过长等信息
            return Response(ser.errors)


class RequestDataDetailView(MyAPIView):

    # 认证类,没有时,注释掉
    authentication_classes = []
    permission_classes = []

    # 单查询
    def get(self, request, *args, **kwargs):
        # 获取所有数据
        db_data = Interface_request_data.objects.filter(pk=kwargs["id"]).all()
        ser = InterfaceDataSerializer(instance=db_data, many=True)  # 序列化
        return Response(ser.data)  # 返回

    # 单条数据更新,演示m2m数据更新,当没有m2m时,手动处理逻辑
    def put(self, request, *args, **kwargs):
        # 获取单条数据
        db_date = Interface_request_data.objects.filter(id=kwargs["id"]).first()
        # 进行序列化
        ser = InterfaceDataSerializer(instance=db_date, data=request.data)
        if ser.is_valid():  # 验证字段,注意:验证字段根据data进行验证
            # 根据save方法,当instance不为空时,调用update方法
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)

    def delete(self, request, *args, **kwargs):
        db_data = Interface_request_data.objects.filter(id=kwargs["id"]).first()
        # 当有关联时,自动删除联系
        db_data.delete()
        return Response()

 

posted @ 2024-12-25 17:24  蜗牛·哥  阅读(12)  评论(0)    收藏  举报