Lavanya Komarasamy
Lavanya Komarasamy

Reputation: 43

dynamic format the string from tuples of tuple using python

In the below, Scenario 1 is working fine in both (Code 1 & Code 2). But Scenario 2 is not working in Code 1.

My requirement is Tuple should keep on repeating itself until it fills all the formatting string in the Query dynamically. Because where clauses are not constant for all the queries.

Scenario 1#

query = SELECT * FROM test.order where total_price in {}

Tuple:

finTup=((125, 125, 125, 125),)
SELECT * FROM test.order where total_price in (125, 125, 125, 125)

Scenario 2#

query = SELECT * FROM test.order WHERE order_id IN {} AND product_id IN {}

Tuple:

finTup=((101, 105, 106, 107), (2, 2, 2, 2))

Code 1:

frt = 'finTup[{}]'
half = ''
val = ''
i = 0
le = len(finTup)
for i in range(le):
    print(i)
    print(eval(frt.format(i)))
    if i == le -1:
        half = half + frt.format(i)
        val = val + " " + frt.format(i)
    else:
        half = half + frt.format(i)+', '
        val = val + " " + frt.format(i)+', '
temp2 = query.format(eval(val))

Code 2:

if le == 1:
     query = query.format(finTup[0])
elif le == 2:
      query = query.format(finTup[0], finTup[1])
 elif le == 3:
       query = query.format(finTup[0], finTup[1], finTup[2])
elif le == 4:
       query = query.format(finTup[0], finTup[1], finTup[2], finTup[3])

Error:
temp2 = query.format(eval(val))
IndexError: tuple index out of range

Please help me to fix this.

Upvotes: 1

Views: 437

Answers (1)

Léopold Houdin
Léopold Houdin

Reputation: 1525

TL;DR

Hello, you have this error because you are trying to provide a single argument to the format function which expects two parameters (since you have 2 {}).

Note: Avoid using the eval function... That's pretty not recommended, even less in the current situation that you are. Everything can be done without having to evaluate a str.


In what follows, I only run the code for Scenario #2, i.e.

query2 = """SELECT * FROM test.order WHERE order_id IN {} AND product_id IN {}"""
finTup = ((101, 105, 106, 107), (2, 2, 2, 2))

Step 1: Debugging

Let's imagine you update your code for debugging as follows:

frt = 'finTup[{}]'
half = ''
val = ''
i = 0 # btw, you don't need to initialize i here
le = len(finTup)

for i in range(le):
    if i == le -1:
        half = half + frt.format(i)
        val = val + " " + frt.format(i)
    else:
        half = half + frt.format(i)+', '
        val = val + " " + frt.format(i)+', '

print val
print eval(val)

temp2 = query.format(eval(val))

Your output should be:

 vars[0],  vars[1]
((101, 105, 106, 107), (2, 2, 2, 2))

So, imagine you write python code from the above output, what you would do is:

query.format(((101, 105, 106, 107), (2, 2, 2, 2)))

You are actually providing a single parameter of type tuple with a two elements. But what's important here is that you provide format with a single parameter. The format function will try to access the second parameter, which you don't provide. It breaks.


Step 2: Fixing the code

Have a look at this SO article. It shows you how to use the * operator. It basically transforms a list or tuple into a sequence of items. Perfect for functions parameters and so forth.

So, applied to your current case:

temp2 = query.format(*eval(val))

And the trick is done.


Step 3: Optimization

Now, let's trash the Code 1 and use what we've learn with Code 2. We need to unpack the tuple of tuple into parameters to feed in to format. So, why not just do:

# I renamed `finTup` to `vars` since I don't know what it means
def get_query(query, vars):
    return query.format(*vars)

It basically combines the logic of Code 1 and Code 2 into a single line.

Upvotes: 2

Related Questions