Tkinter search query in database: TypeError: 'NoneType' object is not iterable - search

I'm unable to solve a problem with a search query in the database (sqlite3) in Tkinter. Parts of my code:
front.py
# Entries
self.name_text = tk.StringVar()
self.entry_name = tk.Entry(self.parent, textvariable=self.name_text)
self.entry_name.grid(row=3, column=1)
self.color_text = tk.StringVar()
self.combobox2=ttk.Combobox(self.parent, textvariable=self.color_text)
self.combobox2["values"] = ('red','blue','white')
self.labelCombobox=ttk.Label(self.parent, textvariable=self.color_text)
self.combobox2.grid(row=4, column=1)
self.parent.bind('<Return>',lambda e:refresh())
def search_command(self):
self.listBox.delete(0,tk.END)
for row in backend.database.search(self.name_text.get(),self.color_text.get()):
self.listBox.insert(tk.END, row)
backend.py class database:
def search(name="",color=""):
try:
connect = sqlite3.connect("color.db")
cur = connect.cursor()
sql = "SELECT * FROM color WHERE name=? OR color=?"
values = (self, name_text.get(), color_text.get())
cur.execute(sql, values)
rows = cur.fetchall()
name_text.set(rows[1])
color_text.set(rows[2])
entry_name.configure('disabled')
combobox2.configure('disabled')
connect.close()
except:
messagebox.showinfo('nothing found!')
I also tried to put a self in in an other version of backend.py. This gives the same error.
def search(self, name="",color=""):
try:
self.connect = sqlite3.connect("color.db")
self.cur = self.connect.cursor()
self.sql = "SELECT * FROM color WHERE name=? OR color=?"
self.values = (self, name_text.get(), color_text.get())
self.cur.execute(sql, values)
self.rows = self.cur.fetchall()
self.name_text.set(rows[1])
self.color_text.set(rows[2])
self.entry_name.configure('disabled')
self.combobox2.configure('disabled')
self.connect.close()
except:
messagebox.showinfo('nothing!')
Please help solve the error:
for row in backend.database.search(self.name_text.get(),self.color_text.get()):
TypeError: 'NoneType' object is not iterable

There are few issues on the backend.database.search() function:
name_text and color_text are undefined
passed arguments name and color should be used in values instead
it does not return any result (this is the cause of the error)
Below is a modified search() function:
def search(name="", color=""):
rows = () # assume no result in case of exception
try:
connect = sqlite3.connect("color.db")
cur = connect.cursor()
sql = "SELECT * FROM color WHERE name=? OR color=?"
values = (name, color) # use arguments name and color instead
cur.execute(sql, values)
rows = cur.fetchall()
connect.close()
except Exception as e:
print(e) # better to see what is wrong
messagebox.showinfo('nothing found!')
return rows # return result

The error TypeError: 'NoneType' object is not iterable means that your query is returning no rows.
That is at least partly because of this code:
sql = "SELECT * FROM color WHERE name=? OR color=?"
values = (self, name_text.get(), color_text.get())
cur.execute(sql, values)
This caused self to be used for the name parameter, and the result of name_text.get() will be associated with the color attribute. The result of color_text.get() is ignored.
You need to remove self - your sql uses two parameters so you need to send it two parameters.
The other problem appears to be that you're iterating over the results of search, but search doesn't return anything. You need to add a return statement to the search function.

Related

Python: Subclassing a dict to have two keys and a defaultvalue

following the two very readable tutorials 1 and 2, I would like to create a dictionary with two keys that gives a defaultvalue in case the key-pair does not exist.
I managed two fullfill the first condition with
from collections import defaultdict
class DictX(dict):
def __getattr__(self, key1 = None, key2 = None):
try:
return self[(key1,key2)]
# This in idea of how to implement the defaultdict. But it does not seem to work
# except KeyError as k::
# self[(key1,key2)] = 0.
# return self[(key1,key2)]
## or just return 0
except KeyError as k:
raise AttributeError(k)
def __setattr__(self, key1, key2, value):
self[(key1,key2)] = value
def __delattr__(self, key):
try:
del self[key]
except KeyError as k:
raise AttributeError(k)
def __repr__(self):
return '<DictX ' + dict.__repr__(self) + '>'
sampledict = DictX()
sampledict[3,5] = 5
sampledict[1,4] = 4
print("Checking the dict ",sampledict[1,4])
# This line is going to throw an error
print("Checking the default dict ",sampledict[3,6])
How do I code the defaultvalue behaviour?
Pro-Question:
If I just give one value sampledict[1,] or sampledict[1,:], I would like to get a list of all key - value pairs that start with 1. Is that possible?

SQL UPDATE statement error in python code

I am trying to write a python function that updates a postgres database. The table to be updated is given (department)
Below is the function I wrote:
def modify_dept_budget(deptName, newBudget):
connection = None
try:
connection = connector() # This is a function I wrote to connect so I can hide my credentials.
cursor1 = connection.cursor()
query1 = f"select dept_name from department"
cursor1.execute(query1)
results1 = cursor1.fetchall()
results1 = [result[0] for result in results1]
if deptName in results1:
idx = results1.index(deptName)
print(results1[idx])
cursor2 = connection.cursor()
query = f"UPDATE department SET budget = %s WHERE dept_name == {deptName}"
cursor2.execute(query, [newBudget])
cursor3 = connection.cursor()
cursor3.execute(f'SELECT * FROM department')
results2 = cursor3.fetchall()
headers = [item[0] for item in cursor3.description]
df = pd.DataFrame(data = results2, columns = headers)
print(df)
except(Exception, psycopg2.DatabaseError) as error:
print(error)
finally:
if connection:
cursor1.close()
cursor2.close()
cursor3.close()
connection.close()
When I run modify_dept_budget('Music', 85000), I get the following error:
Music
column "music" does not exist
LINE 1: ...PDATE department SET budget = 85000 WHERE dept_name == Music
^
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-77-16e7278e3358> in <module>
----> 1 modify_dept_budget('Music', 85000)
<ipython-input-76-80361b6ddf35> in modify_dept_budget(deptName, newBudget)
26 cursor1.close()
27 cursor2.close()
---> 28 cursor3.close()
29 connection.close()
UnboundLocalError: local variable 'cursor3' referenced before assignment
Can anyone help me figure out what is going on?
You have two problems. First, SQL does not use the double == for the equality test. Just plain =. Second, that string literal needs to be quoted. Unless you need a table name or a field name, you won't be using f-strings with SQL. So, do:
query = "UPDATE department SET budget = %s WHERE dept_name = %s"
cursor2.execute(query, [newBudget, deptName])

AttributeError: 'tuple' object has no attribute Latitude

Can someone please explain to me, why when I write like this everything works fine:
def create_geo_hash(lat, lon):
latitude = float(lat)
longitude = float(lon)
geo_hash = pygeohash.encode(latitude, longitude, precision=4)
return geo_hash
def fill_with_valid_coords(df: DataFrame) -> DataFrame:
validated_rdd = df.rdd.map(lambda row: check_for_invalid_coords(row))
geo_hash_rdd = validated_rdd.map(lambda row: (create_geo_hash(row[5], row[6]), ))
geo_hash_df = geo_hash_rdd.toDF(schema=['Geo_Hash'])
return geo_hash_df
But when I pass the entire row to the mapping function like this:
geo_hash_rdd = validated_rdd.map(lambda row: create_geo_hash(row))
and change my create_geo_hash function accordingly:
def create_geo_hash(row):
latitude = float(row.Latitude)
longitude = float(row.Longitude)
geo_hash = pygeohash.encode(latitude, longitude, precision=4)
return geo_hash
I am having AttributeError: 'tuple' object has no attribute 'Latitude'
When I pass entire row to the validated_rdd = df.rdd.map(lambda row: check_for_invalid_coords(row)) and then use it in another function as a row it works fine.

Can we use keyword arguments in UDF

Question I have is can we we use keyword arguments along with UDF in Pyspark as I did below. conv method has a keyword argument conv_type which by default is assigned to a specific type of formatter however I want to specify a different format at some places. Which is not getting through in udf because of keyword argument. Is there a different approach of using keyword argument here?
from datetime import datetime as dt, timedelta as td,date
tpid_date_dict = {'69': '%d/%m/%Y', '62': '%Y/%m/%d', '70201': '%m/%d/%y', '66': '%d.%m.%Y', '11': '%d-%m-%Y', '65': '%Y-%m-%d'}
def date_formatter_based_on_id(column, date_format):
val = dt.strptime(str(column),'%Y-%m-%d').strftime(date_format)
return val
def generic_date_formatter(column, date_format):
val = dt.strptime(str(column),date_format).strftime('%Y-%m-%d')
return val
def conv(column, id, conv_type=date_formatter_based_on_id):
try:
date_format=tpid_date_dict[id]
except KeyError as e:
print("Key value not found!")
val = None
if column:
try:
val = conv_type(column, date_format)
except Exception as err:
val = column
return val
conv_func = functions.udf(conv, StringType())
date_formatted = renamed_cols.withColumn("check_in_std",
conv_func(functions.col("check_in"), functions.col("id"),
generic_date_formatter))
So the problem is with the last statement(date_formatted = renamed_cols.withColumn("check_in_std",
conv_func(functions.col("check_in"), functions.col("id"),
generic_date_formatter)))
Since the third argument generic_date_formatter is a keyword argument.
On trying this I get following error:
AttributeError: 'function' object has no attribute '_get_object_id'
Unfortunately you cannot use udf with keyword arguments. UserDefinedFunction.__call__ is defined with positional arguments only:
def __call__(self, *cols):
judf = self._judf
sc = SparkContext._active_spark_context
return Column(judf.apply(_to_seq(sc, cols, _to_java_column)))
but the problem you have is not really related to keyword arguments. You get exception because generic_date_formatter is not a Column object but a function.
You can create udf dynamically:
def conv(conv_type=date_formatter_based_on_id):
def _(column, id):
try:
date_format=tpid_date_dict[id]
except KeyError as e:
print("Key value not found!")
val = None
if column:
try:
val = conv_type(column, date_format)
except Exception as err:
val = column
return val
return udf(_, StringType())
which can be called:
conv_func(generic_date_formatter)(functions.col("check_in"), functions.col("id"))
Check Passing a data frame column and external list to udf under withColumn for details.

Dictionary with functions versus dictionary with class

I'm creating a game where i have the data imported from a database, but i have a little problem...
Currently i get a copy of the data as a dictionary, which i need to pass as argument to my GUI, however i also need to process some data, like in this example:
I get the data as a dict (I've created the UseDatabase context manager and is working):
def get_user(name: str, passwd: str):
user = {}
user['name'] = name
user['passwd'] = passwd
with UseDatabase() as cursor:
_SQL = "SELECT id, cash, ruby FROM user WHERE name='Admin' AND password='adminpass'"
cursor.execute(_SQL)
res = cursor.fetchall()
if res:
user['id'] = res[0][0]
user['cash'] = res[0][1]
user['ruby'] = res[0][2]
return user
return res
.
.
.
def get_activities():
with UseDatabase() as cursor:
_SQL = "SELECT * FROM activities WHERE user_id='2'"
cursor.execute(_SQL)
res = cursor.fetchall()
if res:
ids = [i[0] for i in res]
activities = {}
for i in res:
activities[i[0]] = {'title':i[1],'unlock':i[2],'usr_progress':i[3]}
return (ids, activities)
return res
Need it as a dict in my GUI ("content" argument):
class SideBar:
def __init__(self, screen: 'pygame.display.set_mode()', box_width: int, box_height: int, content: dict, font: 'font = pygame.font.Font()'):
#content dict: {id: {'title':'','unlock':'','usr_progress':''},...}
self.box_width = box_width
self.box_height = box_height
self.box_per_screen = screen.get_height() // box_height
self.content = content
self.current_box = 1
self.screen = screen
self.font = font
self.generate_bar()
def generate_bar (self):
active = [i for i in self.content.keys() if i in range(self.current_box, self.current_box+self.box_per_screen)]
for i in range(self.box_per_screen):
gfxdraw.box(self.screen,pygame.Rect((0,i*self.box_height),(self.screen.get_width()/3,self.screen.get_height()/3)),(249,0,0,170))
self.screen.blit(self.font.render(str(active[i]) + ' - ' + self.content[active[i]]['title'], True, (255,255,255)),(10,i*self.box_height+4))
for i in range(self.box_per_screen):
pygame.draw.rect(self.screen,(50,0,0),pygame.Rect((0,i*self.box_height),(self.screen.get_width()/3,self.screen.get_height()/3)),2)
But still need to make some changes in the data:
def unlock_act(act_id):
if user['cash'] >= activities[act_id]['unlock'] and activities[act_id]['usr_progress'] == 0:
user['cash'] -= activities[act_id]['unlock']
activities[act_id]['usr_progress'] = 1
So the question is: in this situation should i keep a copy of the data as dict, and create a class with it plus the methods i need or use functions to edit the data inside the dict?

Resources