【问题标题】:How to order queryset based on best match in django-rest-framework?如何根据 django-rest-framework 中的最佳匹配对查询集进行排序?
【发布时间】:2020-10-13 21:59:32
【问题描述】:

我正在尝试排序带有参数的查询结果按匹配数

例如,假设我们有一个模型:

class Template(models.Model):
    headline = CharField(max_length=300)
    text = TextField()
    image_text = TextField(max_length=500, blank=True, null=True)
    tags = TaggableManager(through=TaggedItem)
    ...

使用序列化器:

class TemplateSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Template
        fields = (...)

还有一个 ViewSet:

class TemplateViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows Templates to be viewed or edited.
    """
    queryset = Template.objects.all()
    serializer_class = TemplateSerializer

    def get_queryset(self):
        queryset = Template.objects.all()

        tags = self.request.query_params.getlist('tags', None)
        search_text = self.request.query_params.getlist('search_text', None)

        if tags is not None:
            queries = [Q(groost_tags__name__iexact=tag) for tag in tags]
            query = queries.pop()
            for item in queries:
                query |= item

            queryset = queryset.filter(query).distinct()

        if search_tags is not None:
            queries = [Q(image_text__icontains=string) |
                       Q(text__icontains=string) |
                       Q(headline__icontains=string) for string in search_tags]
            query = queries.pop()
            for item in queries:
                query |= item

            queryset = queryset.filter(query).distinct()

我需要做的是计算过滤器找到的每个匹配项,然后按每个模板的匹配数量对查询集进行排序。例如:

我想找到所有在其文本、image_text 或标题中包含 "hello""world" 字符串的模板。所以我将查询参数 "search_text" 设置为 hello,world。带有 headline="World"text="Hello, everyone." 的模板将有 2匹配headline="Hello" 的另一条将有 1 匹配。具有 2 个匹配项的模板将是查询集中的 firsttagstagssearch_text combined 的行为应该相同。

我尝试直接在 ViewSet 中计算这些数字,然后返回一个 sorted(queryset, key=attrgetter('matches')),但在 DRF 中遇到了几个问题,例如 Template没有“matches”属性。或通过 API 直接访问模板实例时出现 404。

有什么想法吗?

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    尝试annotation,其中每个匹配对返回 1 或 0,并汇总为排名:

    from django.db.models import Avg, Case, F, FloatField, Value, When
    
    Template.objects.annotate(
        k1=Case(
            When(image_text__icontains=string, then=Value(1.0)),
            default=Value(0.0),
            output_field=FloatField(),
        ),
        k2=Case(
            When(text__icontains=string, then=Value(1.0)),
            default=Value(0.0),
            output_field=FloatField(),
        ),
        k3=Case(
            When(headline__icontains=string, then=Value(1.0)),
            default=Value(0.0),
            output_field=FloatField(),
        ),
        rank=F("k1") + F("k2") + F("k3"),
    ).order_by("-rank")
    

    【讨论】:

    • 天才!我认为注释是一种方式,但我不知道如何实现正确的行为。但还有一个问题。标签是标签列表。如何做到这一点,但对于列表,而不是字符串?这意味着,按列表中的最佳匹配排序。如果 tags=hello, world,那么带有 tags=hello 的模板会排在第一位,而带有 tags=hello 的模板会排在第二位.
    • 你看过非 Django 解决方案吗?在这种情况下,Elasticsearch、Solr 或 Sphinx 可以通过可配置的权重提供帮助。
    • 还没有,因为我认为这可以使用 django 完全解决。不过我会试一试,谢谢!
    猜你喜欢
    • 2015-10-06
    • 1970-01-01
    • 1970-01-01
    • 2020-04-16
    • 1970-01-01
    • 2015-10-09
    • 1970-01-01
    • 2021-12-23
    • 1970-01-01
    相关资源
    最近更新 更多