如何使用Django和REST框架按公共值分页?

2024-09-29 20:22:28 发布

您现在位置:Python中文网/ 问答频道 /正文

背景:

嗨,各位。我有一个模型Transplant,它描述了在给定时间移植的植物种类。以下是我的模型,归结为一个简单的形式:

class Species(models.Model):
    name = models.TextField()

class Transplant(models.Model):
    datetime = models.DateTimeField()
    species = models.ManyToManyField(Species, related_query_name='transplants')

问题:

我想提供API的结果,这些结果由我的DateTimeField的日期组件分页,并显示日期和不同物种的列表。每天有任意数量的移植手术,移植手术在任意的日子进行。你知道吗

考虑到以下移植:

[
    { 'datetime': '2019-07-01 10:00:00', 'species': ['Lettuce', 'Kale'] },
    { 'datetime': '2019-07-01 12:00:00', 'species': ['Basil', 'Kale', 'Chard'] },
    { 'datetime': '2019-07-01 15:00:00', 'species': ['Lettuce'] },

    { 'datetime': '2019-07-02 11:00:00', 'species': ['Spinach'] },
    { 'datetime': '2019-07-02 16:15:00', 'species': ['Lettuce'] },

    { 'datetime': '2019-07-05 09:00:00', 'species': ['Lettuce', 'Kale'] },
    { 'datetime': '2019-07-05 12:00:00', 'species': ['Arugula', 'Spinach'] },
]

我想要这些结果(如果page_size = 2):

# page 1
[
    { 'date': '2019-07-01', 'species': ['Lettuce', 'Kale', 'Basil', 'Chard'] },
    { 'date': '2019-07-02', 'species': ['Spinach', 'Lettuce'] },
]

# page 2
[
    { 'date': '2019-07-05', 'species': ['Lettuce', 'Kale', 'Arugula', 'Spinach'] },
]

我尝试过的:

我现在要做的是查询不同的日期,抓取一页这些日期,再次查询那些日期的所有移植,然后序列化,就像这样:

class TransplantViewSet(viewsets.ModelViewSet):
    model            = Transplant
    queryset         = Transplant.objects.all()
    serializer_class = GenericTransplantSerializer
    lookup_field     = 'pk'
    # ...

    @list_route(methods=('get'))
    def by_date(self, request, *args, **kwargs):
        # Annotate dates
        date_qs = Transplants.objects.order_by('-datetime')\
            .annotate(date=Trunc('datetime', 'day', output_field=DateField()))\
            .values_list('date', flat=True)\
            .distinct()
        # Get a single page of dates
        dates = paginator.paginate_queryset(dates_qs, request)
        # Get matching transplants
        transplants = Transplant.objects.filter(datetime__date__in=dates)\
            .annotate(date=Trunc('datetime', 'day', output_field=DateField()))
        # Build a dict formatted as
        # { 'date_1': [t1, t2, ...], 'date_2': [t1, t2, ...], ... }
        transplants_by_date = {}
        for t in transplants:
            transplants_by_date.setdefault(t.date, []).append(t)
        # Transpose to a list of dicts in the format
        # { 'date': 'date_1': 'species': [t1, t2, ...], ... }
        transplants_by_date = [{'date': k, 'species': v} for k,v in transplants_by_date.items()]
        # Serialize
        serial = TransplantByDateSerializer(transplants_by_date,
                                            many=many,
                                            context={'request':request})

        return Response(serial.data)

有没有不那么笨手笨脚的方法?这是可行的,但它会造成额外的数据库访问,而且性能不是很好(实际的模型和查询更复杂,在某种程度上得益于预取)。你知道吗

我曾尝试使用window w as (partition by date_part('day', datetime) order by datetime desc)(用Django实现)这样的方法在一次数据库访问中完成,但我无法将结果转换成Model实例,也无法避免滚动我自己的字典,如上所述。你知道吗

我愿意把我的DateTimeField改成DateField,如果这有帮助的话,因为在我实际的、更复杂的模型中,确切的时间是在别处捕捉到的。这个Transplant模型更多的是对活动的总结。你知道吗

我使用的是django2.1.7、drf3.7.3和python3.6.8,如果这很重要的话,它们都运行在postgresql11之上。你知道吗

任何建议都将不胜感激!谢谢!你知道吗


Tags: 模型datetimedatebymodelsrequestpageclass

热门问题