Django Rest Framework: How do I order/sort a search/filter query?

pyramidface picture pyramidface · Jul 17, 2015 · Viewed 7.1k times · Source

I'm building out an API with Django Rest Framework, and I'd like to have a feature that allows users to search by a query. Currently, http://127.0.0.1:8000/api/v1/species/?name=human yields:

{
    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"
                }
            ]
        }
    ]
}

It's pretty close to what I want, but not quite there. I'd like it so that the first object inside results would be the one with the id of 4 since the name field is the most relevant to the search query (?name=human). (I don't really care about how the rest is ordered.) It seems that currently it is sorting the results by ascending id. Anyone know a good way to handle this? Thanks!

Here is my api folder's 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

Answer

piglei picture piglei · Jul 17, 2015

You want to sort search result by relevance, in your case name: "Human" should be the best result because it exactly matchs the query word.

If it's only to solve the problem, your could use raw sql query to achieve your goal, which like:

# 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")

This query will find all record which contains "human"(ignore cases), and sort the result by length of name field desc. which name: "Human" will be the first item to show up.


FYI, Database query usually is not the best approach to do such kind of stuff, you should go check djang-haystack project which helps you build search engine upon django project, fast and simple.