【问题标题】:Django Rest Framework: How do I order/sort a search/filter query?Django Rest Framework:如何对搜索/过滤查询进行排序/排序?
【发布时间】:2015-10-06 14:21:44
【问题描述】:

我正在使用 Django Rest Framework 构建一个 API,并且我想要一个允许用户通过查询进行搜索的功能。现在, http://127.0.0.1:8000/api/v1/species/?name=human 产量:

{
    count: 3,
    next: null,
    previous: null,
    results: [
        {
            id: 1,
            name: "Humanoid",
            characters: [
                {
                    id: 46,
                    name: "Doctor Princess"
                }
            ]
        },
        {
            id: 3,
            name: "Inhuman (overtime)",
            characters: [

            ]
        },
        {
            id: 4,
            name: "Human",
            characters: [
                {
                    id: 47,
                    name: "Abraham Lincoln"
                }
            ]
        }
    ]
}

它非常接近我想要的,但并不完全在那里。我希望results 中的第一个对象是id 为4 的对象,因为name 字段与搜索查询最相关(?name=human)。 (我并不真正关心其余部分是如何排序的。)目前它似乎正在通过升序id 对结果进行排序。有谁知道处理这个的好方法?谢谢!

这是我的 api 文件夹的 views.py

class SpeciesFilter(django_filters.FilterSet):
    name = django_filters.CharFilter(name="name", lookup_type=("icontains"))
    class Meta:
        model = Species
        fields = ['name']

class SpeciesViewSet(viewsets.ModelViewSet):
    queryset = Species.objects.all()
    serializer_class = SpeciesSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    # search_fields = ('name',)
    filter_class = SpeciesFilter

【问题讨论】:

    标签: python django rest django-rest-framework django-filter


    【解决方案1】:

    我同意 django-haystack 上的@piglei,但我认为按字段值长度排序是一个糟糕的主意, 而且也没有必要为此编写 SQL。更好的方法是:

     Species.objects.all().extra(select={'relevance': 'char_length(full_name)', order_by=['relevance'])  # PostgreSQl
    

    仍然很糟糕,即使是快速修复。 如果你真的不想设置 django-haystack,一个稍微不那么糟糕的方法是使用 python 对结果进行排序:

    from difflib import SequenceMatcher
    
    species = Species.objects.all()
    
    species = sorted(species,
                     lambda s: SequenceMatcher(None, needle.lower(), s.name.lower()).quick_ratio(),
                     reverse=True)
    

    我没有测试此代码,因此请告诉我它是否不起作用以及是否需要帮助将其集成到 DRF 中。

    这仍然很糟糕的原因是 difflib 的搜索算法与用于搜索数据库的算法不同,因此您可能永远不会真正获得使用 difflib 比 __icontains 可能获得的结果更相关的结果找。更多信息在这里:Is there a way to filter a django queryset based on string similarity (a la python difflib)?

    编辑:

    虽然试图举一个例子说明为什么按字段值长度排序是一个糟糕的主意,但我实际上已经设法说服自己,与__icontains 一起使用它可能不是那么糟糕的主意。我会留下这样的答案,因为它可能对某人有用或有趣。示例:

    needle = 'apple'
    haystack = ['apple', 'apples', 'apple computers', 'apples are nice']  # Sorted by value length
    

    【讨论】:

      【解决方案2】:

      您希望按相关性对搜索结果进行排序,在您的情况下,name: "Human" 应该是最好的结果,因为它与查询词完全匹配。

      如果只是为了解决问题,您可以使用原始 sql 查询来实现您的目标,例如:

      # NOT TESTED, sql expression may vary based on which database you are using
      queryset = Species.objects.raw("select * from species where lower(name) like '%human%' order by char_length(name) desc limit 20")
      

      此查询将查找所有包含“human”的记录(忽略大小写),并按名称字段 desc 的长度对结果进行排序。其中name: "Human" 将是第一个显示的项目。


      仅供参考,数据库查询通常不是做这类事情的最佳方法,你应该去查看djang-haystack 项目,它可以帮助你在 django 项目上构建搜索引擎,快速而简单。

      【讨论】:

        猜你喜欢
        • 2015-12-06
        • 1970-01-01
        • 2021-01-02
        • 1970-01-01
        • 1970-01-01
        • 2017-01-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多