Django query case-insensitive list match

前端 未结 7 1147
独厮守ぢ
独厮守ぢ 2020-12-03 09:21

I have a list of names that I want to match case insensitive, is there a way to do it without using a loop like below?

a = [\'name1\', \'name2\', \'name3\']
         


        
相关标签:
7条回答
  • 2020-12-03 10:01

    I am expanding Exgeny idea into an two liner.

    import functools
    Name.objects.filter(functools.reduce(lambda acc,x: acc | Q(name_iexact=x)), names, Q()))
    
    0 讨论(0)
  • 2020-12-03 10:09

    Unfortunatley, there are no __iin field lookup. But there is a iregex that might be useful, like so:

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

    or even:

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

    Note that if a can contain characters that are special in a regex, you need to escape them properly.

    NEWS: In Django 1.7+ it is possible to create your own lookups, so you can actually use filter(name__iin=['name1', 'name2', 'name3']) after proper initialization. See documentation reference for details.

    0 讨论(0)
  • 2020-12-03 10:10

    Keep in mind that at least in MySQL you have to set utf8_bin collation in your tables to actually make them case sensitive. Otherwise they are case preserving but case insensitive. E.g.

    >>> 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>]
    

    So, if portability is not crucial and you use MySQL you may choose to ignore the issue altogether.

    0 讨论(0)
  • 2020-12-03 10:13

    Adding onto what Rasmuj said, escape any user-input like so

    import re
    result = Name.objects.filter(name__iregex=r'(' + '|'.join([re.escape(n) for n in a]) + ')')
    
    0 讨论(0)
  • 2020-12-03 10:19

    In Postgresql you could try creating a case insensitive index as described here:

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

    Then run a query:

    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)
    

    Index search will run faster than the regex matching query.

    0 讨论(0)
  • 2020-12-03 10:22

    Another way to this using django query functions and annotation

    from django.db.models.functions import Lower
    Record.objects.annotate(name_lower=Lower('name')).filter(name_lower__in=['two', 'one']
    
    0 讨论(0)
提交回复
热议问题