Сравнить строку в алфавитном порядке django db - PullRequest
4 голосов
/ 21 июня 2019

У меня есть следующая модель:

class Page(Model):
      book = ForeignKey(Book, on_delete=CASCADE)
      page = IntegerField()
      start = CharField(max_length=350, db_index=True)
      end = CharField(max_length=350, db_index=True)

Как запросить базу данных, чтобы получить страницы, которые «содержат» данное слово?

page1 = Page.objects.create(start='beaver', end='brother')
page2 = Page.objects.create(start='boy', end='brother')
  • Page.objects.filter («грудь» между началом и концом) должен вернуть page1 и page2.
  • Page.objects.filter («зверь» между началом и концом) ничего не должен возвращать.
  • Page.objects.filter («блок» между началом и концом) должен возвращать единственную страницу1, поскольку block в алфавитном порядке после бобра и до брата .

Поиск должен выполняться без учета регистра.

Итак, мне нужно написать запрос, который выбирает все строки, где start по алфавиту "меньше", чем данное слово, а end по алфавиту "больше", чем данное слово.

Ответы [ 3 ]

1 голос
/ 08 июля 2019

Одним из вариантов будет преобразование всех значений базы данных для start и end в верхний или нижний регистр перед сохранением.Затем для поиска используйте фильтры __gte и __lte (после преобразования поискового запроса в верхний или нижний регистр).

Мне кажется, что это работает (используя Python 3.6, Django 2.2,postresql 10):

# create with lowercase words
Page.objects.create(start='beaver', end='brother')
Page.objects.create(start='boy', end='brother')

# filter using lowercase as well
v = request.GET.get('search_term', '').lower()
qs = Page.objects.filter(start__lte=v, end__gte=v)

Или я неправильно понял ваш вопрос?

1 голос
/ 08 июля 2019

Вы можете преобразовать end и start в десятичные числа.

В вашей модели используйте DecimalField вместо CharField.

И тогда вы можете использовать таблицу ascii для преобразования ваших слов.

Например, «любовь» будет означать: 108 111 118 101

Итак, в базе данных оно должно иметь значение: 0.108111118101 (значения должны быть <1, чтобы длина слова не мешала фильтрации). </p>

"amour" будет означать: 97 109 111 117 114

Обратите внимание, что код ascii "a" имеет только 2 цифры, и все буквы должны иметь одинаковые цифры (здесь 3), поэтому добавьте 0, если это так: +0,097109111117114

Тогда легко выполнить запрос, чтобы увидеть, находится ли десятичный разделитель между ними или нет, используя значение меньше (lt) и больше (gt)

Примечания:

  • Вы можете использовать геттеры и сеттеры ваших моделей для перевода слова в их значения ascii и наоборот.

  • Преобразование слов в нижний регистр, иначе оно не будет работать с таблицей ascii, так как 'C' имеет значение, отличное от 'c', например

  • Использование таблицы ascii не будет работать с тем, чего нет в латинском алфавите. Например, ç, é, à, è, ù, вероятно, прервут поиск, вам следует подумать о создании собственной таблицы или замене этих букв их базовыми буквами ...

Теперь давайте посмотрим, находится ли «джанго» между «любовью» и «любовью»:

love   : 0.108111118101
django : 0.100106097110103111 
amour  : 0.097109111117114

Да, это так:)

0 голосов
/ 12 июля 2019

Мой ответ применим только к Postgresql, но здесь может быть одно решение:

У Django с postgresql есть CICharField поле модели в django.contrib.postgres.fields.Это также поддерживает индексирование строки без учета регистра.Строки будут по-прежнему храниться в правильном регистре, но операции сравнения будут нечувствительными к регистру.

from django.contrib.postgres.field import CICharField

class Page(Model):
      book = ForeignKey(Book, on_delete=CASCADE)
      page = IntegerField()
      start = CICharField(max_length=350, db_index=True)
      end = CICharField(max_length=350, db_index=True)

Это должно примерно решить вашу проблему, вы сможете использовать gte и lteфильтры и сравнение будет без учета регистра.Он должен прекрасно обрабатывать юникод в зависимости от настроек вашей базы данных.

t1 = "breast"
t2 = "beast"
t3 = "block"
page1 = Page.objects.create(start='beaver', end='brother')
page2 = Page.objects.create(start='boy', end='brother')
Page.objects.filter(start__lte=t1, end__gte=t1)  # <QuerySet [<Page: Page start=beaver, end=brother>, <Page: Page start=boy, end=brother>]>
Page.objects.filter(start__lte=t2, end__gte=t2)  # <QuerySet []>
Page.objects.filter(start__lte=t3, end__gte=t3)  # <QuerySet [<Page: Page start=beaver, end=brother>]>

t4 = "Ù"  # Between Ø and Ú
t5 = "Ü"  # Not between Ø and Ú
page3 = Page.objects.create(start='Ø', end='Ú')
Page.objects.filter(start__lte=t4, end__gte=t4) # <QuerySet [<Page: Page start=Ø, end=Ú>]>
Page.objects.filter(start__lte=t5, end__gte=t5) # <QuerySet []>

Это изменение приведет к миграции, которая устанавливает CITextExtension() в базу данных и изменяет столбцы.Вам может нужно разделить этот файл миграции на две миграции, где первая установит CITextExtension, а вторая изменит ваши существующие столбцы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...