【问题标题】:Django how to sort and paginate nested ORM and non-ORM objectDjango如何对嵌套的ORM和非ORM对象进行排序和分页
【发布时间】:2022-02-09 19:10:56
【问题描述】:

我需要进行查询以获取一些属性,并使用其category FK 在另一个表中获取每月成本的数据。结果,需要按天、分期、总价和到期日计算价值。

一切正常,但需要按 Property 模型的某些字段排序数据,按总价排序并对结果进行分页。

我可以同时进行排序和分页工作,但不能同时进行。

另外,我想问一下那些带有installments 的计算是否适合放在serializer.py,或者应该移动到viewset.py,或其他地方?

到目前为止,这是我的代码:

带分页的ViewSet.py:

class MyViewSet(viewsets.ModelViewSet):
    queryset = MyModel.Property.objects.filter(status)\
        .prefetch_related('owner__user')\
        .prefetch_related('host__user')\
        .prefetch_related('category__user')

    serializer_class = MySerializer
    pagination_class = StandardResultsSetPagination
    filterset_class = MyFilterSet
    filter_backends = [filters.OrderingFilter, dj_filters.DjangoFilterBackend]


    def get_queryset(self):
        today = str(datetime.now())
        start_date = self.request.query_params.get('start_date')
        end_date = self.request.query_params.get('end_date')

        '''
        validate start_date and end_date
        '''

        return self.queryset

带有排序的ViewSet.py:

class MyViewSet(viewsets.ModelViewSet):
    queryset = MyModel.Property.objects.filter(status)\
        .prefetch_related('owner__user')\
        .prefetch_related('host__user')\
        .prefetch_related('category__user')

    serializer_class = MySerializer
    # pagination_class = StandardResultsSetPagination
    filterset_class = MyFilterSet
    filter_backends = [filters.OrderingFilter, dj_filters.DjangoFilterBackend]

    def get_queryset(self):
        today = str(datetime.now())
        start_date = self.request.query_params.get('start_date')
        end_date = self.request.query_params.get('end_date')

        '''
        validate start_date and end_date
        '''

        return self.queryset

    def list(self, request, *args, **kwargs):
        response = super().list(request, args, kwargs)
        ordering = request.query_params.get('ordering')

        if ordering:
            response.data = sorted(
                response.data,
                key=operator.itemgetter(ordering.replace('-', ''),)
            )

            if "-" in ordering:
                response.data = sorted(
                    response.data,
                    key=lambda k: (k[ordering.replace('-', '')], ),
                    reverse=True
                )
            else:
                response.data = sorted(
                    response.data,
                    key=lambda k: (k[ordering], )
                )
        return Response(response.data)

序列化器.py

class MySerializer(serializers.ModelSerializer):
    monthly_price = serializers.SerializerMethodField()
    total_price = serializers.SerializerMethodField()

    class Meta:
        model = MyModel.Property

    def get_monthly_price(self, instance):
        start_date = self.context['request'].query_params['start_date']
        end_date = self.context['request'].query_params['end_date']

        months=helper.calc_months(start_date, end_date)
        '''
        calc months between start_date and end_date
        '''

        prices = MyModel.Category_month_price.objects.filter(
            category=instance.category_location.id,
            month__in=months)

        installments = helper.calc_installments(dates, start_date, prices)
        '''
        installments is a list of dicts with installments due dates and values like:
        [{
            "instalment": 0,
            "value": 1000,
            "due_date": 2022-01-01,
        }, ...]
        '''

        total_price = sum([x['value'] for x in installments])
        self.total_price = total_price

        return installments

    def get_total_price(self, instance):
        return self.total_price

还在list Method 尝试了 whit Paginator,但没有找到构建下一页和上一页链接的方法

from django.core.paginator import Paginator
from django.core.paginator import EmptyPage
from django.core.paginator import PageNotAnInteger

def list(self, ...) :
    """
    ...
    """
    paginator = Paginator(response.data, 25)
    page = self.request.GET.get('page')
    try:
        result = paginator.page(page)
    except PageNotAnInteger:
        result = paginator.page(1)
    except EmptyPage:
        result = paginator.page(paginator.num_pages)

    return Response({
        "count": "",
        "next": "",
        "previous": "",
        "results": []
    )

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    您尝试在查询集中使用 order_by 吗?

    我认为这是解决这个问题的更好方法

    class MyViewSet(viewsets.ModelViewSet):
        queryset = MyModel.Property.objects.filter(status)\
            .prefetch_related('owner__user')\
            .prefetch_related('host__user')\
            .prefetch_related('category__user')
    
        serializer_class = MySerializer
        # pagination_class = StandardResultsSetPagination
        filterset_class = MyFilterSet
        filter_backends = [filters.OrderingFilter, dj_filters.DjangoFilterBackend]
     ...
    
        def list(self, request, *args, **kwargs):
            response = super().list(request, args, kwargs)
            ordering = request.query_params.get('ordering')
    
            if ordering:
                response.data = self.queryset.order_by('category__user')
    
            return Response(response.data)
    

    我试图展示一个使用 order_by 返回数据的表单

    也许你稍微改变一下你的模型以使查询更容易

    class MyModel(models.Model):
        category= models.ForeignKey(User, on_delete=models.CASCADE)
        # ...
    
        class Meta:
            ordering = ['category']
    
    

    参考:https://docs.djangoproject.com/en/4.0/ref/models/options/

    【讨论】:

      【解决方案2】:

      我找到了一个简单的解决方案:

      class MyViewSet(viewsets.ModelViewSet):
          queryset = MyModel.Property.objects.filter(status)\
              .prefetch_related('owner__user')\
              .prefetch_related('host__user')\
              .prefetch_related('category__user')
      
          serializer_class = MySerializer
          filterset_class = MyFilterSet
          filter_backends = [filters.OrderingFilter, dj_filters.DjangoFilterBackend]
      
      
          def get_queryset(self):
              today = str(datetime.now())
              start_date = self.request.query_params.get('start_date')
              end_date = self.request.query_params.get('end_date')
      
              '''
              validate start_date and end_date
              '''
      
              return self.queryset
      
          def list(self, request, *args, **kwargs):
              response = super().list(request, args, kwargs)
              response.request = self.request
              ordering = request.query_params.get('ordering')
      
              # ORDERING WORKING
              if ordering:
                  response.data = sorted(
                      response.data,
                      key=operator.itemgetter(ordering.replace('-', ''),)
                  )
      
                  if "-" in ordering:
                      response.data = sorted(
                          response.data,
                          key=lambda k: (k[ordering.replace('-', '')], ),
                          reverse=True
                      )
                  else:
                      response.data = sorted(
                          response.data,
                          key=lambda k: (k[ordering], )
                      )
      
              # HERE IS ALL I NEED TO DO TO GET PAGINATION WORK
              paginator = StandardResultsSetPagination()
              result_page = paginator.paginate_queryset(response.data, request)
      
              return paginator.get_paginated_response(result_page)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-08-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-25
        • 1970-01-01
        • 2019-06-25
        • 2023-04-11
        相关资源
        最近更新 更多