Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django reverse to contains/icontains

In this question was solved problem for reverse LIKE operation in SQL, for example if field name is "Peter Johnson", we could find it by such query:

select name from user where "Mr. Peter Johnson" like CONCAT('%', name, '%')

Is there any way to do such thing in Django Q object (I'm building a big query, so using raw SQL query will not be rational)?

like image 906
Sergey Burma Avatar asked Mar 17 '13 20:03

Sergey Burma


People also ask

How to use reverse function in Django?

This is the major use of reverse function in Django. The redirect variable is the variable here which will have the reversed value. So the reversed url value will be placed here. So for the name of the url mentioned in the redirect_url_name, a url value will be allocated in the backend.

How to do reverse likes in Django ORM?

Unfortunately, Django's ORM doesn't have anything built-in for reverse LIKEs. But an .extra () clause may make it a bit easier than a raw query. qs.extra ( where= ['''%s LIKE %s.%s'''], params= ( "string to match", FooModel._meta.db_table, "bar_field", ), )

How do I find similar data in Django using PostgreSQL?

If you are on the latest version of Django (1.10 or later) and using Postgres the ORM can handle this. Check out the docs. A trigram_similar lookup will get you what you are looking for: Don't forget to enable this lookup by enabling the pg_tgrm extension. You can do that with a django migration.

What is the use of reverse function in Python?

The reverse function can be considered very useful in url handling and it helps to avoid any future breakdown in the application because of its capability to hold url values in it. The functioning of reverse function can be divided into three straightforward methods as like below, 1) Declare the url to be framed in the url’s.py file.


2 Answers

While extras do provide an expanded complex functionality for edge cases, extras should be treated as last resort and likely going to be deprecated at some point.

This can be accomplished using annotate and filtering.

from django.db.models import F, Value, CharField

MyUserModel.objects \
     .annotate(my_name_field=Value('Mr. Peter Johnson', output_field=CharField())) \
     .filter(my_name_field__icontains=F('name'))

Genericized:

from django.db.models import F, Value, CharField

@staticmethod
def reverse_case_insensitive_contains(model, search_field_name: str, search_field_value: str):
    return model.objects \
        .annotate(search_field=Value(search_field_value, output_field=CharField())) \
        .filter(search_field__icontains=F(search_field_name))
like image 187
rphan Avatar answered Sep 19 '22 15:09

rphan


Unfortunately, Django's ORM doesn't have anything built-in for reverse LIKEs. But an .extra() clause may make it a bit easier than a raw query.

I used something like this:

qs.extra(
    where=['''%s LIKE %s.%s'''],
    params=(
        "string to match",
        FooModel._meta.db_table,
        "bar_field",
    ),
)

The problems with the code above are that

1) it doesn't work with sqlite backend in this form ("syntax error near .", it does work with table/column names hardcoded in query... which is not always safe and always ugly);

and 2) it requires FooModel.bar_field to have data %in like style% so you can't match arbitrary strings (this can be fixed with a query like %s LIKE CONCAT("%", %s.%s, "%") but it'll make it DBMS-specific, which is not good).

Reversed LIKE itself should probably work with any major DBMS, but I tested it only on sqlite and postgres.

Maybe someone should generalize my solution and create a reusable, DBMS-agnostic app with special subclassed queryset/manager/Q-object for this specific task...

like image 21
Ivan Anishchuk Avatar answered Sep 18 '22 15:09

Ivan Anishchuk