【问题标题】:Django query case-insensitive list matchDjango查询不区分大小写的列表匹配
【发布时间】:2011-02-09 16:26:20
【问题描述】:

我有一个要匹配不区分大小写的名称列表,有没有办法不使用下面的循环来做到这一点?

a = ['name1', 'name2', 'name3']
result = any([Name.objects.filter(name__iexact=name) for name in a])

【问题讨论】:

    标签: django django-queryset


    【解决方案1】:

    不幸的是,没有 __iin 字段查找。但是有一个iregex 可能有用,如下所示:

    result = Name.objects.filter(name__iregex=r'(name1|name2|name3)')
    

    甚至:

    a = ['name1', 'name2', 'name3']
    result = Name.objects.filter(name__iregex=r'(' + '|'.join(a) + ')')
    

    请注意,如果 a 可以包含正则表达式中的特殊字符,则需要正确地 escape 它们。

    新闻:在 Django 1.7+ 中,可以创建自己的查找,因此您可以在正确初始化后实际使用 filter(name__iin=['name1', 'name2', 'name3'])。见documentation reference for details

    【讨论】:

    • Postgres 支持不区分大小写的索引,因此在这种情况下,为每个项目运行单独的“iexact”查询可能比 iregex 匹配更快。在 django 的 postgres 后端“iexact”搜索使用 UPPER() 转换,因此在 UPPER() 上为该行自定义索引可以获得加速。
    • 我希望他们实施 __iin
    • @Evgeny 我希望您能添加答案或给我们一个链接。谢谢!
    • @GrijeshChauhan 当然,看看我下面的帖子。
    • FOR ALL 只需复制此答案:还有两件事需要考虑:(1)正则表达式转义,如下面@Martin Smith 所述,以及(2)如果您想要一个 __in 运算符,请确保您使用的是开始和结束分隔符result = Name.objects.filter(name__iregex=r'^(' + '|'.join([re.escape(b) for b in a]) + ')$')
    【解决方案2】:

    在 Postgresql 中,您可以尝试创建一个不区分大小写的索引,如下所述:

    https://stackoverflow.com/a/4124225/110274

    然后运行查询:

    from django.db.models import Q
    name_filter = Q()
    for name in names:
        name_filter |= Q(name__iexact=name)
    result = Name.objects.filter(name_filter)
    

    索引搜索将比正则表达式匹配查询运行得更快。

    【讨论】:

    • 谢谢!明白了。
    • 小心这段代码!如果变量名称为空,则 .filter 将返回该模型的所有对象!
    • 这可行,但是当列表增长时它会很快变慢
    【解决方案3】:

    使用django query functions 和注释的另一种方法

    from django.db.models.functions import Lower
    Record.objects.annotate(name_lower=Lower('name')).filter(name_lower__in=['two', 'one']
    

    【讨论】:

    • 如果我没记错的话,请记住,这将无法使用“名称”字段中的任何索引,除非该索引已经以Lower('name') 方式创建:stackoverflow.com/a/7005656/1236843
    【解决方案4】:

    加上 Rasmuj 所说的,像这样转义任何用户输入

    import re
    result = Name.objects.filter(name__iregex=r'(' + '|'.join([re.escape(n) for n in a]) + ')')
    

    【讨论】:

      【解决方案5】:

      请记住,至少在 MySQL 中,您必须在表中设置 utf8_bin 排序规则才能真正区分大小写。否则它们会保留大小写但不区分大小写。例如

      >>> models.Person.objects.filter(first__in=['John', 'Ringo'])
      [<Person: John Lennon>, <Person: Ringo Starr>]
      >>> models.Person.objects.filter(first__in=['joHn', 'RiNgO'])
      [<Person: John Lennon>, <Person: Ringo Starr>]
      

      因此,如果可移植性并不重要并且您使用 MySQL,则可以选择完全忽略该问题。

      【讨论】:

        【解决方案6】:

        我正在将 Exgeny 的想法扩展到两个班轮。

        import functools
        Name.objects.filter(functools.reduce(lambda acc,x: acc | Q(name_iexact=x)), names, Q()))
        

        【讨论】:

          【解决方案7】:

          这是一个自定义用户模型 classmethod 的示例,用于过滤用户不区分大小写的电子邮件

          from django.db.models import Q
          
          @classmethod
          def get_users_by_email_query(cls, emails):
              q = Q()
              for email in [email.strip() for email in emails]:
                  q = q | Q(email__iexact=email)
              return cls.objects.filter(q)
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-12-25
            • 2022-06-13
            • 1970-01-01
            • 2021-10-14
            • 1970-01-01
            • 1970-01-01
            • 2020-07-27
            相关资源
            最近更新 更多