Django:额外字段的比较

时间:2011-04-22 13:56:04

标签: python django django-orm

简短的问题: Django是否有办法以不区分大小写的方式根据某个字段的字母顺序查找下一行?

长问题:我在数据库中有一些单词,并为它们提供详细信息视图。我希望能够按字母顺序浏览这些单词。所以我需要按字母顺序找出上一个和下一个单词的id。现在我所做的是以下(原始是存储单词名称的字段):

class Word(models.Model):
    original = models.CharField(max_length=50)
    ...

    def neighbours(self):
        """
        Returns the words adjacent to a given word, in alphabetical order
        """
        previous_words = Word.objects.filter(
            original__lt=self.original).order_by('-original')
        next_words = Word.objects.filter(
            original__gt=self.original).order_by('original')
        previous = previous_words[0] if len(previous_words) else None
        next = next_words[0] if len(next_words) else None
        return previous, next

问题在于这是一个区分大小写的比较,因此Foo出现在bar之前,这不是我想要的。为了避免这个问题,在另一个视图中 - 我列出了所有单词,我使用了一个自定义模型管理器,它添加了一个额外的字段,比如这个

class CaseInsensitiveManager(models.Manager):

    def get_query_set(self):
        """
        Also adds an extra 'lower' field which is useful for ordering
        """
        return super(CaseInsensitiveManager, self).get_query_set().extra(
            select={'lower': 'lower(original)'})

并在Word的定义中添加

objects = models.Manager()
alpha = CaseInsensitiveManager()

通过这种方式,我可以进行像

这样的查询
Word.alpha.all().order_by('lower')

并按字母顺序获取所有单词,无论情况如何。但我不能

class Word(models.Model):
    original = models.CharField(max_length=50)
    ...

    objects = models.Manager()
    alpha = CaseInsensitiveManager()

    def neighbours(self):
        previous_words = Word.objects.filter(
            lower__lt=self.lower()).order_by('-lower')
        next_words = Word.objects.filter(
            lower__gt=self.lower()).order_by('lower')
        previous = previous_words[0] if len(previous_words) else None
        next = next_words[0] if len(next_words) else None
        return previous, next

事实上,Django不会基于field lookups接受extra fields。那么,我应该做什么(没有编写自定义SQL)?

奖金问题:我至少看到了我正在做的更多问题。首先,我不确定性能。我假设在定义previous_wordsnext_words时根本不会执行任何查询,并且当我定义previousnext时,数据库中的唯一查找将会发生,从而产生查询或多或少

SELECT Word.original, ..., lower(Word.original) AS lower
WHERE lower < `foo`
ORDER BY lower DESC
LIMIT 1

这是对的吗?或者我正在做一些会使数据库放慢太多的事情?我不太了解Django ORM的内部工作原理。

第二个问题是我实际上必须处理不同语言的单词。鉴于我知道每个单词的语言,即使它们具有非ASCII字符,也有一种方法可以按字母顺序排列它们。例如,我希望按此顺序拥有méchantmoche,但我得到mocheméchant

1 个答案:

答案 0 :(得分:1)

数据库应该能够为你做这样的排序,它应该能够在没有“低级”功能的情况下进行排序。

您需要修复的是数据库整理和编码。

例如,如果您使用的是mysql,则可以使用字符集utf8和collat​​ion utf8_general_ci

如果该排序规则对您不起作用,您可以根据自己的需要和数据库尝试其他排序规则。但是在查询中使用额外的字段和函数是一个丑陋的解决方法,会降低应用程序的速度。

mysql和postgresql中还有许多collat​​ions选项:

http://dev.mysql.com/doc/refman/5.5/en/charset-mysql.html http://stackoverflow.com/questions/1423378/postgresql-utf8-character-comparison

但这绝对是在数据库级别进行优化的好机会。