【问题标题】:Cant get queryset to return list of objects that is descending by tag matches无法让查询集返回按标签匹配降序的对象列表
【发布时间】:2019-11-25 19:58:37
【问题描述】:

我正在运行 django 2.1.7、DRF 并使用 taggit。我正在编写自己的自定义查询集来查找对象具有的标签。 网址: example.com/api/tags=books,headphones,sleep

应该返回包含对象的 JSON,从包含大多数标签到包含至少一个标签。 这是gitgist

from django.db.models import Case, ExpressionWrapper, IntegerField, Q, Value, When

class SpecialSearch(ListAPIView):
    model = Object
    serializer_class = ObjectSerializer

    def get_queryset(self, rs, value):
        """
        Recipe search matching, best matching and kind of matching,
        by filtering against `tags` query parameter in the URL.
        """
        if value:
            tags = [tag.strip() for tag in value.split(',')]
            qs = Object.objects.filter(
                reduce(
                    lambda x, y: x | y, [Q(tags__icontains=tag) for tag in tags]))
            check_matches = map(
                lambda x: Case(
                    When(Q(tags__icontains=x), then=Value(1)),
                        default=Value(0)),
            tags)
            count_matches = reduce(lambda x, y: x + y, check_matches)
            qs = qs.annotate(
            matches=ExpressionWrapper(
                count_matches,
                output_field=IntegerField()))
            qs = qs.order_by('-matches')
        return qs

目前,我提交的这段代码可以正常工作,但返回的是按对象 ID 排序的 json,并且在提交一系列新标签时,API 端点不会收到来自 API 的新 json 转储。我现在完全迷路了。任何帮助将不胜感激。

【问题讨论】:

  • 嗨,就我而言,您的代码“开箱即用”。不过,您应该使用tags__iexact=tag 来避免与另一个标签的子字符串中的标签不匹配。

标签: django rest django-rest-framework drf-queryset


【解决方案1】:

对于 OP 来说可能为时已晚,但如果有人看到这个,在 count_matches 周围添加 Sum() 可能会奏效:

from django.db.models import (Case, ExpressionWrapper, IntegerField, Q, Value, When, Sum)

class SpecialSearch(ListAPIView):
    model = Object
    serializer_class = ObjectSerializer

    def get_queryset(self, rs, value):
        """
        Recipe search matching, best matching and kind of matching,
        by filtering against `tags` query parameter in the URL.
        """
        if value:
            tags = [tag.strip() for tag in value.split(',')]
            qs = Object.objects.filter(
                reduce(
                    lambda x, y: x | y, [Q(tags__icontains=tag) for tag in tags]))
            check_matches = map(
                lambda x: Case(
                    When(Q(tags__icontains=x), then=Value(1)),
                        default=Value(0)),
            tags)
            count_matches = reduce(lambda x, y: x + y, check_matches)
            qs = qs.annotate(
            matches=ExpressionWrapper(
                Sum(count_matches),
                output_field=IntegerField()))
            qs = qs.order_by('-matches')
        return qs

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-07
    • 2017-08-05
    • 2017-02-08
    • 1970-01-01
    • 2020-09-01
    • 2018-08-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多