比较按字母顺序排序的字符串django db

时间:2019-06-21 09:35:54

标签: python django postgresql django-models

我有以下模型:

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(开始和结束之间的“块”)应返回唯一的page1,因为block按字母顺序在海狸之后和兄弟

搜索应该不区分大小写。

因此,我需要编写一个查询以获取所有行,其中start的字母顺序比给定单词小,而end的字母顺序比给定单词大。

3 个答案:

答案 0 :(得分:1)

您可以将endstart转换为小数。

在模型中,使用DecimalField代替CharField

然后您可以使用ascii表转换您的单词。

例如,“爱”将转换为: 108111118101

因此在数据库中,其值应为: 0.108111118101(值应小于1,以使单词的长度不会影响过滤。)

“爱慕”将转换为: 97109111117114

请注意,“ a”的ASCII码只有2位数字,并且所有字母必须具有相同的数字位数(此处为3),因此,请在这种情况下用0填充: 0.097109111117114

然后使用小于(lt)大于(gt)的值很容易查询小数是否在小数位之间

注释:

  • 您可以使用模型的getter和setter将单词转换为ascii值,反之亦然。

  • 将单词转换为小写,否则将无法与ascii表一起使用,因为“ C”的值不同于“ c”的值

  • 使用ascii表不适用于拉丁字母中不存在的内容。例如,ç,é,à,è,ù可能会破坏搜索,您应该考虑建立自己的表格,或者将这些字母替换为其基本字母...

现在让我们看看“ django”是否介于“爱情”和“爱情”之间:

love   : 0.108111118101
django : 0.100106097110103111 
amour  : 0.097109111117114

是的:)

答案 1 :(得分:1)

一种选择是在保存之前将startend的所有数据库值转换为大写或小写。然后使用__gte__lte过滤器进行搜索(将搜索词也转换为大写或小写之后)。

它似乎对我有用(使用Python 3.6Django 2.2postresql 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)

还是我误解了你的问题?

答案 2 :(得分:0)

我的答案仅适用于Postgresql,但这可能是一种解决方案:

使用postgresql的Django在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)

这应该可以解决您的问题,您将可以使用gtelte过滤器,并且比较将不区分大小写。根据数据库中的设置,它应该可以很好地处理unicode。

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,第二个将修改您现有的列。