zoukankan      html  css  js  c++  java
  • restfulframework引用多对多外键

    记录一下工作中遇到的问题

    最近在写restfulframework,感觉还是很便利的

    首先贴一下文档地址

    https://www.django-rest-framework.org/api-guide/filtering/

    https://www.django-rest-framework.org/api-guide/serializers/

    https://www.django-rest-framework.org/api-guide/relations/#manytomanyfields-with-a-through-model

    使用GernricViewSet可以便捷的新增接口,在类中定义queryset指定模型,用serializer_class指定序列化类,用pagination_class指定分页类,再用filter_backends和filter_class做筛选,可以解决大部分curd问题,如下

    class GitLabCommit(mixins.ListModelMixin, mixins.RetrieveModelMixin,viewsets.GenericViewSet):
        #加RetriveModelMixin可以查询特定模型的信息
        queryset = GitCommit.objects.all()
        serializer_class = CommitSerializer
        pagination_class = MyPageNumberPagination
        filter_backends = (DjangoFilterBackend,)
        filter_class = UserCommitFilter

    其中模型类无需多言,序列化类形如下

    class CommitSerializer(serializers.ModelSerializer):
        author_email = serializers.CharField(source='author.email')
        branch_name = serializers.CharField(source = "branch.branchName")
        branch_project_name = serializers.CharField(source="branch.project.projectName")
    
        class Meta:
            model = GitCommit
            fields = "__all__"

    fields这里也可以写成元组的形式

    fields = ("StaffId","name","staff_name")

    分页类类似这样

    class MyCursorPagination(pagination.CursorPagination):
        """
        Cursor 光标分页 性能高,安全
        """
        page_size = 9
        ordering = '-update_time'
        page_size_query_param = "pages"
        max_page_size = 20
        cursor_query_description = '页面'
        page_size_query_description = '每页条数'
    
    
    
    class MyPageNumberPagination(pagination.PageNumberPagination):
        """
        普通分页,数据量越大性能越差
        """
        page_size = 11
        page_size_query_param = 'size'
        page_query_param = 'page'
        max_page_size = 20

    这是过滤器类,可以直接定义查找字段或者通过方法进行复杂查找

    class UserCommitFilter(filters.FilterSet):
        user_id = filters.NumberFilter(field_name='author__StaffId', lookup_expr='exact')
        start_date = filters.DateFilter(field_name='commitDate', lookup_expr='gte')
        end_date = filters.DateFilter(field_name='commitDate', lookup_expr='lt')
        commit_sum = filters.NumberFilter(method="get_sum")
    
        def get_sum(self,queryset,name,values):
            if values == 1:
                return queryset.annotate(total_addLines = Sum("addLines"),total_delLins = Sum("delLines"),total_totalLins = Sum("totalLines"))

    这里有一个问题:如果序列化所涉及的模型是关联模型怎么办呢?

    可以参考这个

    https://zhuanlan.zhihu.com/p/27667372

    这里特别说一下,对于多对多模型,可以通过嵌套来进行关联,如下

    模型定义

    class Staff(BaseTable):
        StaffId = models.IntegerField(primary_key=True, help_text="工号")
        email = models.CharField(max_length=50,default="",null=True,help_text="邮箱")
        name = models.CharField(max_length=50,default="",null=True,help_text="姓名")
        department = models.ForeignKey(Department,on_delete=models.CASCADE)
    
        def __str__(self):
            return "%s:%s"%(self.StaffId,self.name)
        class Meta:
            db_table = "gitlab_measure_staff"
    
    class GitGroup(BaseTable):
        id = models.AutoField(primary_key=True, help_text="ID")
        name = models.CharField(max_length=100,default="",null=True,help_text="组名称")
        members = models.ManyToManyField(Staff)
    
        class Meta:
            db_table = "gitlab_measure_gitgroup"

    序列化

    class StaffSerializer(serializers.ModelSerializer):
        staff_name = serializers.CharField(source="name")
        class Meta:
            model = Staff
            fields = ("StaffId","name","staff_name")
    
    class GitGroupSerializer(serializers.ModelSerializer):
        members = StaffSerializer(many=True,read_only=True)
        class Meta:
            model = GitGroup
            fields = ("id","name","members")

    如果此时又有一个project类中的group关联到gitgroup,希望在展示的时候展示出组中所有成员该怎么办呢?

    这里可以使用depth指定查询的深度

    class ProjectSerializer(serializers.ModelSerializer):
        gitGroup_name = serializers.CharField(source='gitGroup.name')
        gitGroup_id = serializers.CharField(source="gitGroup.id")
        department_name = serializers.CharField(source="department.name")
    
        class Meta:
            model = GitProject
            fields = "__all__"
            depth = 2

    这样在结果中就能看到展示的组和成员了,因为在serilizers.ModelSerializer中的get_field()方法中会根据调用self.build_field,将depth传入,build_field方法会调用self.buid_nested_field方法来,再返回一个ModelSerializer类,再在外层函数中循环调用来获取层层对象,最多不超过10层

            for field_name in field_names:
                # If the field is explicitly declared on the class then use that.
                if field_name in declared_fields:
                    fields[field_name] = declared_fields[field_name]
                    continue
    
                extra_field_kwargs = extra_kwargs.get(field_name, {})
                source = extra_field_kwargs.get('source', '*')
                if source == '*':
                    source = field_name
    
                # Determine the serializer field class and keyword arguments.
                field_class, field_kwargs = self.build_field(
                    source, info, model, depth
                )
    
                # Include any kwargs defined in `Meta.extra_kwargs`
                field_kwargs = self.include_extra_kwargs(
                    field_kwargs, extra_field_kwargs
                )
    
                # Create the serializer field.
                fields[field_name] = field_class(**field_kwargs)
        def build_field(self, field_name, info, model_class, nested_depth):
            """
            Return a two tuple of (cls, kwargs) to build a serializer field with.
            """
            if field_name in info.fields_and_pk:
                model_field = info.fields_and_pk[field_name]
                return self.build_standard_field(field_name, model_field)
    
            elif field_name in info.relations:
                relation_info = info.relations[field_name]
                if not nested_depth:
                    return self.build_relational_field(field_name, relation_info)
                else:
                    return self.build_nested_field(field_name, relation_info, nested_depth)
    
            elif hasattr(model_class, field_name):
                return self.build_property_field(field_name, model_class)
    
            elif field_name == self.url_field_name:
                return self.build_url_field(field_name, model_class)
    
            return self.build_unknown_field(field_name, model_class)
        def build_nested_field(self, field_name, relation_info, nested_depth):
            """
            Create nested fields for forward and reverse relationships.
            """
            class NestedSerializer(ModelSerializer):
                class Meta:
                    model = relation_info.related_model
                    depth = nested_depth - 1
                    fields = '__all__'
    
            field_class = NestedSerializer
            field_kwargs = get_nested_relation_kwargs(relation_info)
    
            return field_class, field_kwargs
  • 相关阅读:
    软件需求模式阅读笔记02
    软件需求模式阅读笔记1
    问题账户需求分析
    浅谈软件架构师的工作过程
    架构之美阅读笔记五
    架构之美阅读笔记四
    架构之美阅读笔记三
    架构之美阅读笔记二
    架构之美阅读笔记一
    软件需求与分析课堂讨论一
  • 原文地址:https://www.cnblogs.com/wuxie1989/p/10842696.html
Copyright © 2011-2022 走看看