2

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?

1 Answer 1

3

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

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you, that's a nice concept. However, I'm getting a unsupported operand type(s) for +: 'Q' and 'str' error.
So your passed_username is also a Q object?
No, it's just a string, like this: username = "display_name"
I have updated my answer for your case. Hope that is your meaning

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.