开发者

Sort ordering in Django: possible to ignore "The" prefix when sorting?

开发者 https://www.devze.com 2023-03-07 04:26 出处:网络
This seems like the kind of thing Django makes simple, wondering if anyone has any experience with it.

This seems like the kind of thing Django makes simple, wondering if anyone has any experience with it.

I have a table of bands, some of whom are called 'The Geeks' (for example). I want them to appear alphabe开发者_如何学编程tically under 'G'.

I know I can do some fancy SQL to rewrite them on the fly and sort that way, but does Django offer anything built-in so I can keep my nice Band.objects.all() syntax and still filter on a custom sort?

I know I could change my data to include a has_prefix field or something and store the bandname as 'Geeks, The' or whatever, but I'd rather not touch the data itself if possible.

Any tips?


Here's how I did it on a recent Django project:

from django.db import models

SomeClass(models.Model):
    title = models.CharField()

    @property
    def alphabetical_title(self):
        """
        Returns an alphabetical-friendly string of a title attribute.
        """
        title = self.title

        # A list of flags to check each `title` against.
        starts_with_flags = [
            'the ',
            'an ',
            'a '
        ]

        # Check each flag to see if the title starts with one of it's contents.
        for flag in starts_with_flags:
            if title.lower().startswith(flag):
                # If the title does indeed start with a flag, return the title with
                # the flag appended to the end preceded by a comma.
                return "%s, %s" % (title[len(flag):], title[:len(flag)-1])
            else:
                pass
        # If the property did not return as a result of the previous for loop then just
        # return the title.
        return self.title

Since this is a property and not an actual column in the database I need to retrieve a QuerySet first and then sort it after the fact in a view. Here's how I did that:

from operator import attrgetter
from someapp.models import SomeClass

some_objects = SomeClass.objects.all()
sorted_objects = sorted(some_objects, key=attrgetter('alphabetical_title'), reverse=False)

I'm sure you've long since found a solution but I figured it might help to post it anyways as someone else in the future might run into this same problem.


You can't do it with order_by()

You can

  1. Do some fancy SQL. If you want to keep it nice and short, you should write a custom model manager.
  2. Do sorting at Python-level, not SQL-level
  3. Store "sort title" and "display title" separately.


Using the model and a couple hints from the above (by respondcreate), and an adaptation of this answer, I came up with the following one line approach:

mysortedlist = sorted([x for x in SomeClass.all()],
               key=lambda y: re.sub("^(the|a|an) ","",y.title.lower()) )

This generates a list from the QuerySet and sorts it according to a lambda function that replaces the, a, and an at the beginning of a lowercased version of the title.

0

精彩评论

暂无评论...
验证码 换一张
取 消