Change f-string local variable from a while loop? - python

I want to query a sqlite database using a f-string for the query, it is supposed to return a small description given a specific id that i stored in a local variable var. However some fields will be empty for some values of the variable. I'm specially interested in the value 0 has it will always return an empty description and is an isolated case that needs to be addressed differently from other values.
I want to iterate through a range until I find a not empty description field.
Here is an example:
var = str(self.some_global_variable) # In this case consider var = '0'
query = f'my sql query goes here WHERE this_id={var}'
description = self.function_to_fetch_db(query)
while not description and var == '0':
for i in range (1, 31):
var = str(i)
description = self.function_to_fetch_db(query)
print(description, var)
print(query)
The output of this will be something like:
[] 1
my sql query goes here WHERE this_id=0
[] 2
my sql query goes here WHERE this_id=0
[] 3
my sql query goes here WHERE this_id=0
.
.
.
The local variable is updated but the query always keeps the original value from outside the while loop.
I also tried an if...else instead of the while loop but the result is the same.
I don't think the SQLite part of the problem is relevant, it's just to illustrate my specific case, the query works for other values. I'm just having trouble to figure out this local variable and f-string relationship.

There are two answers to your questions: the first one is formal, the second one is correct.
The formal answer: the string is computed once before the loop in your case. If you want it to alter it for every value, move it inside:
for i in range(31):
query = f'my sql query goes here WHERE this_id={i}'
description = self.function_to_fetch_db(query)
The correct answer: use parameterized queries instead, that will look like:
conn.execute('my sql query goes here WHERE this_id=?', (i,))
(The substitution syntax may vary depending on your database / driver.)

you could use a named variable in a string:
var = str(self.some_global_variable) # In this case consider var = '0'
fquery = 'my sql query goes here WHERE this_id={var}'
description = self.function_to_fetch_db(fquery.format(var=var))
while not description and var == '0':
for i in range(1, 31):
query = fquery.format(var=i)
description = self.function_to_fetch_db(query)
print(description, i)
print(query)

Related

%s variable in Query Execution Python 3.8 (pymssql)

I have a python script with a basic GUI that logs into a DB and executes a query.
The Python script also asks for 1 parameter called "collection Name" which is taken from the tkinter .get function and is added as a %s inside the Query text. The result is that each time I can execute a query with a different "Collection name". This works and it is fine
Now, I want to add a larger string of Collection Names into my .get function so I can do cursor.execute a query with multiple collection names to get more complex data. But I am having issues with inputing multiple "collection names" into my app.
Below is a piece of my Query1, which has the %s variable that it then gets from the input to tkinter.
From #Session1
Join vGSMRxLevRxQual On(#Session1.SessionId = vGSMRxLevRxQual.SessionId)
Where vGSMRxLevRxQual.RxLevSub<0 and vGSMRxLevRxQual.RxLevSub>-190
and #Session1.CollectionName in (%s)
Group by
#Session1.Operator
Order by #Session1.Operator ASC
IF OBJECT_ID('tempdb..#SelectedSession1') IS NOT NULL DROP TABLE #SelectedSession1
IF OBJECT_ID('tempdb..#Session1') IS NOT NULL DROP TABLE #Session1
Here, is where I try to execute the query
if Query == "GSMUERxLevelSub" :
result = cursor.execute(GSMUERxLevelSub, (CollectionName,))
output = cursor.fetchmany
df = DataFrame(cursor.fetchall())
filename = "2021_H1 WEEK CDF GRAPHS().xlsx"
df1 = DataFrame.transpose(df, copy=False)
Lastly, here is where I get the value for the Collection name:
CollectionName = f_CollectionName.get()
enter image description here
enter code here
Your issues are due to a list/collection being a invalid parameter.
You'll need to transform collectionName
collection_name: list[str] = ['collection1', 'collection2']
new_collection_name = ','.join(f'"{c}"' for c in collection_name)
cursor.execute(sql, (new_collection_name,))
Not sure if this approach will be susceptible to SQL injection if that's a concern.
Edit:
Forgot the DBAPI would put another set of quotes around the parameters. If you can do something like:
CollectionName = ["foo", "bar"]
sql = f"""
From #Session1
Join vGSMRxLevRxQual On(#Session1.SessionId = vGSMRxLevRxQual.SessionId)
Where vGSMRxLevRxQual.RxLevSub<0 and vGSMRxLevRxQual.RxLevSub>-190
and #Session1.CollectionName in ({",".join(["%s"] * len(CollectionName))})
"""
sql += """
Group by
#Session1.Operator
Order by #Session1.Operator ASC
"""
cursor.execute(sql, (CollectionName,))
EDIT: Update to F-string

SQLite/Python same variable name and column name problem

So i am trying to work out how to use sql lite with python by setting up a simple database full of different students and their courseworks and scores etc....
I am writing a function that updates my database for the score a student got given their id, unit id and the mark. However I have a column in my assignments table that is called mark!
This is what I thought would work....
def mark_assignment(database, studentid, assessmentid, mark):
mark = score
access_database(database,"UPDATE assignments SET mark = score, marked = 1 WHERE assignments.studentid = studentid AND assignments.assessmentid = assessmentid ")
All you have to do is add placeholders in your SQL line while executing.
str.format() is one of the string formatting methods in Python3, which
allows multiple substitutions and value formatting. This method lets
us concatenate elements within a string through positional formatting.
Each parameter in the format function replaces the corresponding placeholder in the string.
Here is a small example for the same:
#Code
first_name = "Heeth"
last_name = "Jain"
print("Hello {} {}".format(first_name, last_name))
#Output
Hello Heeth Jain
SQL Statement would look like:
"UPDATE assignments SET mark = {}, marked = 1 WHERE assignments.studentid = {} AND assignments.assessmentid = {}".format(mark, studentid, assessmentid)
Since you are using a single table in this command, you can also change the statement as follows (I just changed assignments.studentid to studentid and so on:
"UPDATE assignments SET mark = {}, marked = 1 WHERE studentid = {} AND assessmentid = {}".format(mark, studentid, assessmentid)
So Finally, after the changes, it should be something like this:
def mark_assignment(database, studentid, assessmentid, mark):
statement = "UPDATE assignments SET mark = {}, marked = 1 WHERE studentid = {} AND assessmentid = {}".format(mark, studentid, assessmentid)
access_database(database, statement)
Note:
In real-life situations, using str.format() is a bad idea as it can be misused using SQL Injection. But since we only know about this single function and not the entire code, formatting can help here. Adding placeholders like ? are the better options in those cases. Reference: https://realpython.com/prevent-python-sql-injection/

How to select all values in case of default in drop-down menu in sqlite3 query where clause

I want to display a data in QTableWidget according to QComboBoxes. In case of select all gender or select all ages, I want apply select all in the column in sqlite3 query
I want gender to be all
gender = "select all both male and female"
connection.execute("SELECT * FROM child where region=? and hospital=? and ageInMonths=? and gender=?", (region,hospital,ageInMonths,gender))
Welcome to Stackoverflow.
While it's a little tedious, the most sensible way to attack this problem is to build a list of the conditions you want to apply, and another of the data values that need to be inserted. Something like the following (untested) code, in which I assume that the variables are set to None if they aren't required in the search.
conditions = []
values = []
if region is not None:
conditions.append('region=?')
values.append(region)
# And similar logic for each other value ...
if gender is not None:
conditions.append('gender=?')
values.append(gender)
query = 'SELECT * FROM child'
if conditions:
query = query + ' WHERE ' + ' AND '.join(conditions)
connection.execute(query, values)
This way, if you want to include all values of a column you simply exclude if from the conditions by setting it to None.
You can build your where clause and your parameter list conditionally.
Below I am assuming that the ageInMonths and gender variables actually contain the value 'all' when this is selected on your form. You can change this to whichever value is actually passed to your code, if it is something different.
When it comes to your actual query, the best way to get all values for a field is to simply exclude it from the where clause of your query entirely.
So something like:
query_parameters = []
query_string = "SELECT * FROM child where region=? and hospital=?"
query_parameters.append(region)
query_parameters.append(hospital)
if ageInMonths != 'all':
query_string += " and ageInMonths=?"
query_parameters.append(ageInMonths)
if gender != 'all':
query_string += " and gender=?"
query_parameters.append(gender)
connection.execute(query_string, query_parameters)
Basically, at the same time we are testing and building the dynamic parts of the SQL statement (in query_string), we are also dynamically defining the list of variables to pass to the query in query_parameters, which is a list object.

Searching a field in SQL to see if it contains a python variable

I am using the Canari Framework to build a series of transforms within Malteog that scan a MySQL database and return the results as entities.
I've got the entire thing working bar the LIKE operator within the SQL statement. Here's the code:
#===========================================================================
# Request and set target from graph
target = (request.entity.value)
#===========================================================================
# Extract message column and check against target entity
statement = (
"SELECT * FROM tableT.TTO WHERE Text LIKE %(tq)s"
)
cursor.execute(statement, { 'tq': target })
results = cursor.fetchall()
#===========================================================================
# Create response entities based on SQL output
for r in results:
e = getTTO(target)
e.From = r[0]
e.Text = r[1]
e.date = r[2]
e.iconurl = 'http://local.paterva.com/phrase.png'
response += e
It works, but it's only returning matches that are exact. I need it to check the TEXT column to see if the entries contain any mention of the variable "target". I spent the best part of a day getting the python variable passed in, but I can't get it to return anything other than an exact match.
The SQL LIKE operator needs special "wildcard" markers ("%" in MySQL) before and/or after the value (depending on if you want a "starts with", "endswith" or "contains" search). You have to add those wildcards around your argument, ie:
cursor.execute(statement, {'tq': "%{}%".format(target)})
Also note that cursor.fetchall() will load your whole resultset in memory, which is asking for MemoryError. cursor objects are iterables and it's safer to use them that way:
cursor.execute(statement, {'tq': "%{}%".format(target)})
for row in cursor:
do_something_with(row)

Keyword search using python and sqlite

I have a plugin to a game server that writes down changes made to a map.The database contains entries formatted like this - id INTEGER PRIMARY KEY,matbefore INTEGER, matafter INTEGER, name VARCHAR(50), date DATE. I am trying to create a function that, when given a column name, an integer, string, or tuple of an integer or string, and a keyword, will find the selected entries. so far, this is the code that I have come to -
def readdb(self,keyword,column,returncolumn = "*"):
self.memwrite
if isinstance(keyword, int) or isinstance(keyword,str):
entry = [keyword]
qmarks = ("? OR " * len(entry))[:-4]
statement = 'SELECT all {0} FROM main WHERE {1} is {2}'.format(returncolumn,column,qmarks)
print(qmarks)
self.memcursor.execute(statement, entry)
return(self.memcursor.fetchall())
keyword is a keyword to search for, column is teh column to search in, and returncolumn is the column to return So I was wondering why this code always fetches no rows, EG - Returns None, no matter what I put for the function. It seems to work fine if I do these things in the console, but not if I wrap them in a function
If entry is a list (like in yesterday's question) , it's not going to work.
>>> returncolumn = "*"
>>> column = "name"
>>> entry = ["Able", "Baker", "Charlie"]
>>> qmarks = ("? OR " * len(entry))[:-4]
>>> statement = 'SELECT all {0} FROM main WHERE {1} is {2}'.format(returncolumn,
column,qmarks)
>>> print statement
SELECT all * FROM main WHERE name is ? OR ? OR ?
and what SQLite will see is:
SELECT all * FROM main WHERE name is 'Able' OR 'Baker' OR 'Charlie'
which is not valid syntax because you need =, not is.
Even if you fix that then (using an integer query for example):
SELECT all * FROM main WHERE id = 1 or 2 or 3
you will get mysterious results because that means WHERE ((id = 1) or 2) or 3), not what you think it does ... you need WHERE id = 1 or id = 2 or id = 3 or (reverting to yesterday's question) WHERE id IN (1,2,3)
def readdb(self,keyword,column,returncolumn = "*"):
self.memwrite # 1.
if isinstance(keyword, int) or isinstance(keyword,str): # 2.
entry = [keyword] # 3.
qmarks = ("? OR " * len(entry))[:-4] # 4.
statement = 'SELECT all {0} FROM main WHERE {1} is {2}'.format(returncolumn,column,qmarks)
print(qmarks) # 5.
self.memcursor.execute(statement, entry)
return(self.memcursor.fetchall())
Not sure what this is supposed to
do. Did you mean self.memwrite()?
Can be changed to if
isinstance(keyword, (int,str))
Or better yet, don't test the type.
As you've written it, keyword can
not be a unicode string. Why
restrict like this? In this case I
think it would be better to use
a try...except... block to catch the subsequent
error than to restrict type.
So at best len(entry) = 1. Notice
also, its possible to never reach
this line if keyword is not of type
int or str. In that case, you would
get an error on line (4) since entry
would not be defined..
This could also be written as
qmarks = ' OR '.join(['?']*len(entry))
It avoids the need for the somewhat magic number 4 in ("? OR " *1)[:-4].
What are you seeing here? If it was
empty, that should have been a clue. Also it would be worth running
print(statement) to get a full picture of what is being sent to .execute().
Perhaps try
statement = 'SELECT {0} FROM main WHERE {1} = ?'.format(
returncolumn,column)
In particular, the is should be changed to =.

Categories

Resources