Короткий вопрос: Есть ли в Django способ найти следующую строку, основываясь на алфавитном порядке некоторого поля, без учета регистра?
Длинный вопрос: У меня есть несколько слов в базе данных и подробный вид для них. Я хотел бы иметь возможность просматривать слова в алфавитном порядке. Поэтому мне нужно узнать идентификатор предыдущего и следующего слова в алфавитном порядке. Сейчас я делаю следующее (оригинал - это поле, в котором хранится название слова):
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 не будет принимать полевых поисков на основе дополнительных полей . Итак, что я должен делать (если не писать собственный SQL)?
Бонусные вопросы: Я вижу, по крайней мере, больше проблем в том, что я делаю. Во-первых, я не уверен в производительности. Я предполагаю, что никакие запросы вообще не выполняются, когда я определяю previous_words
и next_words
, и единственный поиск в базе данных будет происходить, когда я определяю previous
и next
, получая запрос, который более или менее
SELECT Word.original, ..., lower(Word.original) AS lower
WHERE lower < `foo`
ORDER BY lower DESC
LIMIT 1
Это правильно? Или я делаю что-то, что слишком сильно замедляет работу базы данных? Я не знаю достаточно деталей о внутренней работе Django ORM.
Вторая проблема заключается в том, что мне действительно приходится справляться со словами на разных языках. Учитывая, что я знаю язык для каждого слова, есть ли способ получить их в алфавитном порядке, даже если они имеют символы не ASCII. Например, я хотел бы иметь méchant
, moche
в этом порядке, но я получаю moche
, méchant
.