Search code examples
djangodjango-ormsql-likecontains

How to not escape a character in a Django ORM?


I use Django, ORM.

I don't want the character '%' to be escaped.

name=''
author=''
annotation = 'harry%magic%school'

criterion_name = Q(name__contains=cleaned_data['name'])
criterion_author = Q(author__contains=cleaned_data['author'])
criterion_annotation = Q(annotation__contains=cleaned_data['annotation'])

Book.objects.filter(criterion_name, criterion_author, criterion_annotation)

I get '%harry\%magic\%school%':

select name, author, annotation from books 
where name LIKE '%%' AND 
author LIKE '%%' AND 
annotation LIKE '%harry\%magic\%school%'

I want to get '%harry%magic%school%':

select name, author, annotation from books 
where name LIKE '%%' AND 
author LIKE '%%' AND 
annotation LIKE '%harry%magic%school%'

How to fix it?


Solution

  • As you can see in the docs it will escape automatically both % and _, there is no option to not escape it by using built-in lookups.

    So, you could use .extra to write a raw SQL which is undesirable in most cases, even for the most complex ones. Or, write a custom lookup:

    lookups.py

    class Like(models.Lookup):
        lookup_name = "like"
    
        def as_sql(self, compiler, connection):
            lhs, lhs_params = self.process_lhs(compiler, connection)
            rhs, rhs_params = self.process_rhs(compiler, connection)
            params = lhs_params + rhs_params
            return "%s LIKE %s" % (lhs, rhs), params
    
    
    models.CharField.register_lookup(Like)
    

    views.py

    from .lookups import Like
    
    def some_view(request):
        name = ''
        author = ''
        annotation = '%harry%magic%school%'
    
        books = Book.objects.filter(
            Q(name__contains=name), 
            Q(author__contains=author), 
            Q(annotation__like=annotation)
        )
        print(books.query)
        return render(request, 'blank.html')
    
    

    Which yields the following query (the output of print):

    SELECT "myapp_book"."id", "myapp_book"."name", "myapp_book"."author", "myapp_book"."annotation" 
    FROM "myapp_book" 
    WHERE (
        "myapp_book"."name" LIKE %% ESCAPE '\' 
        AND "myapp_book"."author" LIKE %% ESCAPE '\' 
        AND "myapp_book"."annotation" LIKE %harry%magic%school%
    )
    

    Obs: All your criteria are against the name field, so I just assumed it was wrong and changed it for each variable to match its respective field.