sqe
sqe

Reputation: 1716

Use passed parameter as a value in query string in django

Is it possible to use a passed parameter as a value in a django query string?

So, let's pretend I want to search for specific terms (in term_list) in different fields, like username, display_name or name.

Instead of doing this multiple times:

q1_username = Q(username__icontains=term_list[0])
q1_email = Q(email__icontains=term_list[0])

q2_username = Q(display_name__icontains=term_list[0])
q2_email = Q(e_mail__icontains=term_list[0])

q3_username = Q(name__icontains=term_list[0])
q3_email = Q(emailaddress__icontains=term_list[0])

I want to be able to do this in a more generic way for different cases:

q1_username = "display_name"
q1_email = "emailaddress"
q1 = build_query(term_list, q1_username, q1_email)

def build_query(term_list, passed_username, passed_email):
    q_username = Q(insert_passed_username_here__icontains=term_list[0])
    q_email = Q(insert_passed_email_here__icontains=term_list[0])
    # ...
    return query

If I try it like that, I get, not surprisingly, the following error message: Cannot resolve keyword 'insert_passed_username_here' into field.

How can I use the passed_username and passed_email to replace the insert_passed_username_here and insert_passed_email_here in the function?

EDIT: According to the answer from @Tsang-Yi Shen I updated my function but I'm still getting errors. So let's edit my question like this, to make my case more clear:

I have several usernames and emails in different databases like this:

"""query 1"""
q1_username = Q(username__icontains=term_list[0])
q1_email = Q(email__icontains=term_list[0])

special_q1 = q1_username | q1_email
for term in term_list[1:]:
    special_q1.add((Q(username__icontains=term) | Q(email__icontains=term)), special_q1.connector)

"""query 2"""
q2_username = Q(name__icontains=term_list[0])
q2_email = Q(email__icontains=term_list[0])

special_q2 = crimenetwork_username | crimenetwork_email
for term in term_list[1:]:
    special_q2.add((Q(name__icontains=term) | Q(email__icontains=term)), special_q2.connector)

After building this, I then filter the results like this:

context = {}
context["q1_user_list"] = Users1.objects.using("db1").filter(special_q1)
context["q2_user_list"] = Users2.objects.using("db2").filter(special_q2)

Problem: I have about 30 queries like this and want to be able to this in a more generic way.

How can I achieve this with like dictionary unpacking?

Upvotes: 2

Views: 99

Answers (1)

Tsang-Yi Shen
Tsang-Yi Shen

Reputation: 542

I often create a dictionary first then unpack it such as:

def build_query(term_list, passed_username, passed_email):
    q_username_dict = {passed_username + '__icontains': term_list[0]}
    q_username = Q(**q_username_dict)
    q_email_dict = {passed_email + '__icontains': term_list[0]}
    q_email = Q(**q_email_dict)

Though I'm still finding a more graceful solution to this.

Edit:

Since you are finally put all your Qs into a filter(), I suggest to create a list of Q objects:

def make_query(passed_username, passed_email, term_list):
     return [
         (Q(**{passed_username + '__icontains': term}) |
          Q(**{passed_email + '__icontains': term})
         for term in term_list
     ]

Then unpack the list:

q_list = make_query('username', 'email', ['term01', 'term02'])
context["q1_user_list"] = Users1.objects.using("db1").filter(*q_list)

Also I eliminate the use of .add() and .connector since it should be equivalent to an AND operation between Qs. Hope that I did not misunderstand your meaning

Upvotes: 3

Related Questions