You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现双模型数据按日期排序的统一响应并支持分页?

问题描述

现有两个Django模型:

class A(models.Model):
    ...
    date=models.DateTimeField(auto_add_now=True)
    ...
class B(models.Model):
    ...
    date=models.DateTimeField(auto_add_now=True)
    ...

两者都包含date字段,需要编写一个GET接口向前端返回两个模型的所有数据并按日期倒序排序,且已为两个模型编写了包含全部字段的序列化器A_serializer和B_serializer。

当前实现逻辑如下:

A = A.objects.all()
B = B.objects.all()

aSerializer = A_serializer(A, many=True)
bSerializer = B_serializer(B, many=True)

all = aSerializer.data + bSerializer.data
all = sorted(all, key=lambda item: item['date'],reverse=True)

return Response(all)

但该方式无法实现分页等功能,寻求更优实现方案。

优化方案

方案一:数据库层面合并查询(优先推荐)

这种方式把合并和排序逻辑放到数据库层完成,能利用数据库的索引优化,性能更好,尤其适合数据量较大的场景,同时天然支持DRF的原生分页。

实现步骤

  1. 用values()提取两个模型需要的字段,额外添加一个model_type字段区分数据属于A还是B;
  2. 通过union合并两个查询集,再按date倒序排序;
  3. 使用DRF的分页类处理合并后的查询集;
  4. 遍历分页结果,根据model_type找到对应模型实例并序列化。

代码示例

from django.db.models import Value, CharField
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response

# 自定义分页类(可根据需求调整参数)
class CustomPagination(PageNumberPagination):
    page_size = 10
    page_size_query_param = 'page_size'
    max_page_size = 100

def combined_list(request):
    # 提取A模型的字段并标记类型
    a_queryset = A.objects.all().values(
        'id', 'date',  # 替换为A的实际字段
        # 其他字段...
    ).annotate(model_type=Value('A', output_field=CharField()))
    
    # 提取B模型的字段并标记类型
    b_queryset = B.objects.all().values(
        'id', 'date',  # 替换为B的实际字段
        # 其他字段...
    ).annotate(model_type=Value('B', output_field=CharField()))
    
    # 合并查询集并按日期倒序排序
    combined_queryset = a_queryset.union(b_queryset).order_by('-date')
    
    # 分页处理
    paginator = CustomPagination()
    paginated_items = paginator.paginate_queryset(combined_queryset, request)
    
    # 序列化数据
    serialized_data = []
    for item in paginated_items:
        if item['model_type'] == 'A':
            obj = A.objects.get(id=item['id'])
            serialized_data.append(A_serializer(obj).data)
        else:
            obj = B.objects.get(id=item['id'])
            serialized_data.append(B_serializer(obj).data)
    
    return paginator.get_paginated_response(serialized_data)

方案二:自定义分页类适配已合并的列表

如果不想改动查询逻辑,只是需要给现有合并后的列表加分页,可以自定义一个支持列表分页的类,直接对序列化后的合并数据进行分页处理。这种方式适合数据量较小的场景,不用修改数据库查询逻辑。

代码示例

from rest_framework.pagination import PageNumberPagination
from rest_framework.exceptions import NotFound

class ListPagination(PageNumberPagination):
    page_size = 10
    page_size_query_param = 'page_size'
    max_page_size = 100

    def paginate_list(self, data, request):
        self.page_size = self.get_page_size(request)
        paginator = self.django_paginator_class(data, self.page_size)
        page_number = request.query_params.get(self.page_query_param, 1)
        
        # 处理"last"页码参数
        if page_number in self.last_page_strings:
            page_number = paginator.num_pages
        
        try:
            self.page = paginator.page(page_number)
        except self.django_paginator_class.InvalidPage as exc:
            raise NotFound(f"Invalid page {page_number}: {str(exc)}")

        self.request = request
        self.display_page_controls = paginator.num_pages > 1 and self.template is not None
        return list(self.page)

def combined_list(request):
    # 原有查询和序列化逻辑
    a_queryset = A.objects.all()
    b_queryset = B.objects.all()
    
    a_serialized = A_serializer(a_queryset, many=True).data
    b_serialized = B_serializer(b_queryset, many=True).data
    
    combined_data = sorted(a_serialized + b_serialized, key=lambda x: x['date'], reverse=True)
    
    # 应用自定义分页
    paginator = ListPagination()
    paginated_data = paginator.paginate_list(combined_data, request)
    
    return paginator.get_paginated_response(paginated_data)

方案三:基于DRF GenericView的混合视图

如果想让代码更贴合DRF的视图规范,可以自定义一个GenericAPIView子类,把合并、排序、分页逻辑整合到视图中。

代码示例

from rest_framework.generics import GenericAPIView
from rest_framework.pagination import PageNumberPagination

class CombinedListView(GenericAPIView):
    pagination_class = PageNumberPagination

    def get(self, request, *args, **kwargs):
        # 获取两个模型的查询集
        a_queryset = A.objects.all()
        b_queryset = B.objects.all()
        
        # 序列化数据
        a_data = A_serializer(a_queryset, many=True).data
        b_data = B_serializer(b_queryset, many=True).data
        
        # 合并并排序
        combined_data = sorted(a_data + b_data, key=lambda x: x['date'], reverse=True)
        
        # 分页处理
        paginator = self.pagination_class()
        paginated_data = paginator.paginate_queryset(combined_data, request)
        
        return paginator.get_paginated_response(paginated_data)

内容的提问来源于stack exchange,提问作者Learning Developer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 07:55:28