Reputation: 1716
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
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.
Since you are finally put all your Q
s 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 Q
s. Hope that I did not misunderstand your meaning
Upvotes: 3