【问题标题】:Django: comparison on extra fieldsDjango:额外字段的比较
【发布时间】:2011-04-22 13:56:04
【问题描述】:

小问题: 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)'})

我在词的定义中添加

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 不会接受基于extra fieldsfield lookups。那么,我该怎么办(没有编写自定义 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

【问题讨论】:

    标签: python django django-orm


    【解决方案1】:

    数据库应该能够为您进行这种排序,并且它应该能够在没有“lower”功能的情况下这样做。

    真正需要修复的是数据库排序和编码。

    例如,如果您使用 mysql,则可以使用字符集 utf8 和排序规则 utf8_general_ci

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

    在 mysql 和 postgresql 中也有许多排序规则选项:

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

    但这绝对是一个在db层面优化的好机会。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-12-17
    • 2016-10-18
    • 2015-07-20
    • 2020-09-19
    • 2019-12-30
    • 1970-01-01
    • 1970-01-01
    • 2017-04-02
    相关资源
    最近更新 更多