restfulframework引用多對多外鍵

記錄一下工做中遇到的問題git

最近在寫restfulframework,感受仍是很便利的django

首先貼一下文檔地址api

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

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

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

使用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

其中模型類無需多言,序列化類形以下gitlab

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")

分頁類相似這樣ui

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
相關文章
相關標籤/搜索