-1

This script takes user input (for employee) and adds via SQLAlchemy to a sqlite3 .db. It worked fine until I added try: except clause(s) in the add_data method for the age field (planning on adding more try/except clauses to the other fields). The age field needs to be an int chosen by the user. I realized I needed a way to catch any errors caused by the user adding a string to the field instead of a number. The relevant code. . .

try:
        self.age = self.age_var.get()
    except ValueError:
        showinfo("Error:", "Please Enter A Number In Age Field.")

However when I do this, it creates a problem because age now becomes a local variable (as Backtrack pointed out in another post) and I get the UnboundLocalError: local variable 'age' referenced before assignment. I tried changing the code by separating the retrieving of the data and adding to the database, like this. . .

def get_data(self):        
    self.name = self.name_var.get()
    try:
        self.age = self.age_var.get()
    except ValueError:
        showinfo("Error:", "Please Enter A Number In Age Field.")
    self.addr = self.address_var.get()
    self.city = self.city_var.get()
    self.state = self.state_var.get()
    self.zip = self.zip_var.get()   
    self.ssn = self.ssn_var.get()
    self.phone = self.phone_var.get()
    self.cell = self.cell_var.get()

def add_data(self):
    self.get_data()
    # create new Employee in .db
    new_person = Employee(name=self.name, age=self.age, address=self.addr, city=self.city, state=self.state, zip=self.zip, ssn=self.ssn, phone=self.phone, cell=self.cell)
    session.add(new_person)
    session.commit()
    session.close()
    self.callback()
    return

However, I have the same problem in a different guise. Now I get AttributeError: GUI instance has no attribute 'age'. I'm not sure how to get around these problems. I tried adding a default value to age before the try/except clause self.age=0 which does correct the error, but creates another problem. .if a user enters a string instead of an int, the showinfo box appears with the warning, but when the user clicks 'ok', the default value '0' is automatically added to the database instead of giving the user another chance to enter a number for the age. Of course I could just change the age field to a String in Class Employee, but this isn't what I want. I'm lost as what to do. Any help is greatly appreciated! Anyway, here's the original code. . .

from datetime import datetime
from Tkinter import *
from tkMessageBox import *
import sqlite3, sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, DateTime, Integer, String, MetaData
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# map classes to tables through Base Class
Base = declarative_base()


class Employee(Base):
    __tablename__ = 'employee'
    id = Column(Integer, primary_key=True)
    name = Column(String(250))
    age = Column(Integer)
    address = Column(String(250))
    city = Column(String(10))
    state = Column(String(2))
    zip = Column(Integer)
    ssn = Column(String(12))
    phone = Column(String(12))
    cell = Column(String(12))

# create engine for Session connection
engine = create_engine('sqlite:///employee.db')

# create all tables in engine ('CREATE TABLE' in raw SQL)
Base.metadata.create_all(engine)

# create configured 'Session' class
Session = sessionmaker(bind=engine)

# create session
session = Session()


class GUI:
    def __init__(self, parent):
        self.parent = parent

        # create top frame
        frame = Frame(parent)
        frame.pack(expand=YES, fill=BOTH, padx=5, pady=5)

        # create bottom frame
        btm_frame = Frame(parent, relief=SUNKEN, borderwidth=1)
        btm_frame.pack(side=BOTTOM, expand=YES, fill=BOTH, padx=5, pady=5)

        # create datetime object
        d = datetime.today()
        date = d.strftime("%Y-%m-%d")


#------------------------Create Labels-------------------------------#


        # label to display date
        date_label = Label(btm_frame, text=date)
        date_label.pack(side=LEFT)

        # name label
        name_label = Label(frame, text="Enter Name:")
        name_label.grid(row=0, sticky=W)

        # age label
        age_label = Label(frame, text="Enter Age:")
        age_label.grid(row=1, sticky=W)

        # address label
        addr_label = Label(frame, text="Enter Address:")
        addr_label.grid(row=2, sticky=W)

        # city label
        city_label = Label(frame, text="Enter City:")
        city_label.grid(row=3, sticky=W)

        # state label
        state_label = Label(frame, text="Enter State:")
        state_label.grid(row=4, sticky=W)

        # zip code label
        zip_label = Label(frame, text="Enter Zip Code:")
        zip_label.grid(row=5, sticky=W)

        # ssn label
        ssn_label = Label(frame, text="Enter Social Security #:")
        ssn_label.grid(row=6, sticky=W)

        # phone label
        phone_label = Label(frame, text="Enter Phone #:")
        phone_label.grid(row=7, sticky=W)

        # cell label
        cell_label = Label(frame, text="Enter Cell #:")
        cell_label.grid(row=8, sticky=W)


#----------------------Create Vars and Entry-------------------------#


        # name variable and entry
        self.name_var = StringVar()
        self.e1 = Entry(frame, textvariable=self.name_var)
        self.e1.grid(row=0, column=1)

        # age variable and entry
        self.age_var =  IntVar()
        self.e2 = Entry(frame, textvariable=self.age_var)
        self.e2.grid(row=1, column=1)

        # address variable and entry
        self.address_var = StringVar()
        self.e3 = Entry(frame, textvariable=self.address_var)
        self.e3.grid(row=2, column=1)

        # city variable and entry
        self.city_var = StringVar()
        self.e4 = Entry(frame, textvariable=self.city_var)
       self.e4.insert(0, "Roanoke")        # insert default value
       self.e4.grid(row=3, column=1)

        # state variable and entry
        self.state_var = StringVar()
        self.e5 = Entry(frame, textvariable=self.state_var)
        self.e5.insert(0, "VA")             # insert default value
        self.e5.grid(row=4, column=1)

        # zip code variable and entry
        self.zip_var = IntVar()
        self.e6 = Entry(frame, textvariable=self.zip_var)
        self.e6.grid(row=5, column=1)

        # s.s.n variable and entry
        self.ssn_var = StringVar()
        self.e7 = Entry(frame, textvariable=self.ssn_var)
        self.e7.grid(row=6, column=1)

    # phone variable and entry
        self.phone_var = StringVar()
        self.e8 = Entry(frame, textvariable=self.phone_var)
        self.e8.grid(row=7, column=1)

        # cell variable and entry
        self.cell_var = StringVar()
        self.e9 = Entry(frame, textvariable=self.cell_var)
        self.e9.grid(row=8, column=1)

        # quit, search, clear, add, delete buttons
        quit_button = Button(btm_frame, text="Quit", relief=GROOVE, command=parent.destroy)
        quit_button.pack(side=RIGHT)        

        search_button = Button(btm_frame, text="Search", relief=GROOVE, command=self.search)
        search_button.pack(side=RIGHT, padx=1, pady=1)

        clear_button = Button(btm_frame, text="Clear", relief=GROOVE, command=self.clear_entries)
        clear_button.pack(side=RIGHT, padx=1, pady=1)        

        del_button = Button(btm_frame, text="Delete", relief=GROOVE, command=self.del_employee)
        del_button.pack(side=RIGHT, padx=1, pady=1)

        add_button = Button(btm_frame, text="Add", relief=GROOVE, command=self.add_data)
        add_button.pack(side=RIGHT, padx=1, pady=1)


    def add_data(self):        
        name = self.name_var.get()
        age = self.age_var.get()        
        addr = self.address_var.get()
        city = self.city_var.get()
        state = self.state_var.get()
        zip = self.zip_var.get()
        ssn = self.ssn_var.get()
        phone = self.phone_var.get()
        cell = self.cell_var.get()
        # create new Employee in .db
        new_person = Employee(name=name, age=age, address=addr, city=city, state=state, zip=zip, ssn=ssn, phone=phone, cell=cell)
        session.add(new_person)
        session.commit()
        session.close()
        self.callback()
        return        

    def callback(self):        
        showinfo("New Employee", "Data Added")
        self.clear_entries()
        return        

    def clear_entries(self):
        entries = [self.e1, self.e2, self.e3, self.e4, self.e5, self.e6, self.e7, self.e8, self.e9]
        for entry in entries:
            entry.delete(0, END)
        return            

    def search(self):
        search_win = Toplevel()
        search_win.title("Employee Search")

        # create labels for each employee attribute
        attr =   ["Id","Name","Age","Address","City","State","Zip","SSN","Cell","Phone"]
        column = 0
        for a in attr:
                Label(search_win, text=a).grid(row=0,column=column,padx=2, pady=2)
                column += 1

        # search all employees, put each emp. in Entry Widget with For Loop
        res = session.query(Employee).all()
        row = 1
        column = 0
        for employee in res:
            txt = [employee.id, employee.name, employee.age, employee.address, employee.city, employee.state, employee.zip, employee.ssn, employee.phone, employee.cell]
            for t in txt:
                ent = Entry(search_win, relief=RIDGE, width=19)
                ent.grid(row=row, column=column, sticky=W, padx=1, pady=1)
                ent.insert(0, t)
                column += 1
            row += 1
            column = 0
        return

    def del_employee(self):
        del_win = Toplevel()
        del_win.title("Delete Employee")

        id_label = Label(del_win, text="Enter Employee Id:")
        id_label.grid(row=0, column=0, padx=5, pady=5)

        self.employee_id = IntVar()
        self.e10 = Entry(del_win, textvariable=self.employee_id)
        self.e10.grid(row=0, column=1, padx=5, pady=5)

        del_button = Button(del_win, text="Delete Employee", relief=GROOVE, command=self.erase)
        del_button.grid(row=0, column=2, padx=5, pady=5)
        return

    def erase(self):
        emp_id = self.employee_id.get()
        res = session.query(Employee).filter(Employee.id==emp_id).first()
        session.delete(res)
        session.commit()
        showinfo("Employee", "Data Deleted")
        return



if __name__ == '__main__':
    root = Tk()
    root.geometry("310x270")
    root.title("Employee Info")
    mygui = GUI(root)
    root.mainloop()

Here's the first error I got after changing the code above, adding the try: except clause to the age variable in the add_data method. . .In other words, change the add_data method above to this:

def add_data(self):        
    name = self.name_var.get()
    try:
        age = self.age_var.get()
    except ValueError:
        showinfo("Error:", "Please Enter A Number In Age Field.")
    addr = self.address_var.get()
    city = self.city_var.get()
    state = self.state_var.get()
    zip = self.zip_var.get()   
    ssn = self.ssn_var.get()
    phone = self.phone_var.get()
    cell = self.cell_var.get()
    # create new Employee in .db
    new_person = Employee(name=name, age=age, address=addr, city=city, state=state, zip=zip, ssn=ssn, phone=phone, cell=cell)
    session.add(new_person)
    session.commit()
    session.close()
    self.callback()
    return

And I get this error when user inputs a string in the age field then clicking "ok" in the showinfo dialogue box):

    Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 1532, in __call__
    return self.func(*args)
  File "C:\Python27\test2_gui.py", line 182, in add_data
    new_person = Employee(name=name, age=age, address=addr, city=city, state=state, zip=zip, ssn=ssn, phone=phone, cell=cell)
UnboundLocalError: local variable 'age' referenced before assignment
Chris Kavanagh
  • 451
  • 1
  • 5
  • 15
  • Are you saying you think `self.age` is a local variable? If so, that's not true. Can you please show us the actual error and stack trace? I suggest reading your error messages more closely -- they are telling you exactly what the problem is. The error "GUI instance has no attribute age" is correct: your GUi class has no attribute `age`. Why do you think it does or should? – Bryan Oakley Mar 17 '15 at 11:47
  • Why do I think it has an age variable? You don't see the code `age= self.age_var.get()` in the add_data method? Not to mention the script (without the try:except) works perfectly. . .It was only when I added the try:except that I began getting the error. Here's the full error: – Chris Kavanagh Mar 18 '15 at 00:02
  • Exception in Tkinter callback Traceback (most recent call last): File "C:\Python27\lib\lib-tk\Tkinter.py", line 1532, in __call__ return self.func(*args) File "C:/Python27/test3-gui.py", line 185, in add_data new_person = Employee(name=self.name, age=self.age, address=self.addr, city=self.city, state=self.state, zip=self.zip, ssn=self.ssn, phone=self.phone, cell=self.cell) AttributeError: GUI instance has no attribute 'age' – Chris Kavanagh Mar 18 '15 at 00:03
  • If I run the original code (above) changing the add_data method to `try: age = self.age_var.get() except ValueError: showinfo("Error:", "Please Enter A Number In Age Field.")` I get this error. . . – Chris Kavanagh Mar 18 '15 at 00:14
  • Exception in Tkinter callback Traceback (most recent call last): File "C:\Python27\lib\lib-tk\Tkinter.py", line 1532, in __call__ return self.func(*args) File "C:\Python27\test2_gui.py", line 182, in add_data new_person = Employee(name=name, age=age, address=addr, city=city, state=state, zip=zip, ssn=ssn, phone=phone, cell=cell) UnboundLocalError: local variable 'age' referenced before assignment – Chris Kavanagh Mar 18 '15 at 00:14
  • Edit: When I run the original code (above) adding the try: except clause to the age variable it runs fine as long as a number is put in the age field. If I put a string in the age field, I get the showinfo box warning the user to enter a number. . .when I click 'ok' in the box, then I get the "UnboundLocalError: local variable 'age' referenced before assignment" error. The code ran perfectly before I added the try: except clause. It was only after I added it to the age var in the add_data method that I began getting the error. – Chris Kavanagh Mar 18 '15 at 00:52
  • Please put the actual error message and stack trace _in the question_. It's impossible to decipher when it's in a comment. – Bryan Oakley Mar 18 '15 at 01:53
  • According to this Bryan, the try: except clause that I put the age variable in makes it a local variable. That's why I was confused when you said it wasn't. . .http://stackoverflow.com/questions/29069783/getting-unboundlocalerror-local-variable-age-referenced-before-assignment-err – Chris Kavanagh Mar 18 '15 at 04:04
  • So this leads me to ask, how do I get around this? What's the proper way to code this, because I ran out of ideas? – Chris Kavanagh Mar 18 '15 at 04:06
  • The link points to code like this: `try: age = ...`; the code at the top of your question is `try: self.age = ...`. There's a significant difference between the two. – Bryan Oakley Mar 18 '15 at 10:29
  • That stackoverflow question you linked to is wrong. The "try" does not magically make something local. Code in a try block is the same scope as code immediately outside the try. – Bryan Oakley Mar 18 '15 at 10:50

1 Answers1

0

At the start of your question you have a block of code, and you follow it with "age now becomes a local variable". That is impossible, because a) the code you show neither sets nor uses a variable named age, and b) that's not what try does. Your code sets self.age but that's an entirely different thing from a local variable named age.

The error message is telling you all you need to know. If it says "local variable 'age' referenced before assignment.", then you can believe that it is telling the literal truth. You have to ask yourself either "why is age (not self.age) local when it I think it's global?", or "why is age not set when I think that it is?". A try statement in and of itself will _not_ magically make a variable local.

Let me repeat that last line: A try statement in and of itself will _not_ magically make a variable local. The other answer you linked to in the comments gave you bad advice.

A simple low-tech way to understand what is happening is to put a print statement right after assigning the local variable age and you'll see that that line of code isn't getting executed. You need to step through your logic to see why it's not being executed.

If the real problem is that it's complaining about a local variable age and you're setting an attribute named self.age, that's the problem -- change age to self.age, and again, put a print statement where you're setting self.age to prove to yourself that it is -- or isn't -- being set before you get the error.

Unfortunately, you've made a mess of your question. I think there's a couple different versions of the code, and an error message that goes with "the original version" and no error with the current version. I think. It's really hard to understand.

probably the problem is simply that, if your try catches an error when setting a variable, then that variable won't get set. If additional code follows the try and uses the variable, it will fail because the try block failed to set the variable. A simple solution might be to set the variable to zero in the catch block.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • I agree that I've made a mess of the question. . .However, there is no code in the entire script `self.age`. There's only `age=self.age_var.get()` in the add_data method. So, how can I be setting `self.age` when there is no `self.age`? – Chris Kavanagh Mar 18 '15 at 19:24
  • @ChrisKavanagh: the very first block of code in your question has this line of code: `self.age = self.age_var.get()`. See the `self.age`? – Bryan Oakley Mar 18 '15 at 19:48
  • As far as the rest of your comment, I agree and I understand now. Although setting age to 0 in the catch block won't accomplish what I want. It cures the error, but automatically sets the age to 0 if it's not set correctly by the user. . .I'm still confused by you saying there's no local age variable in the add_data method. Is `age=self.age_var.get()` not setting a local variable named `age` in the method? – Chris Kavanagh Mar 18 '15 at 19:58
  • @ChrisKavanagh: this is due to your confusing question. There is no `age=` in the _first_ `add_data` function. Again, your question is very unclear. It's really hard to help when you present two versions of code, because I don't know what the real code is. – Bryan Oakley Mar 18 '15 at 20:00
  • Ok, I understand what you're saying. That's why I presented the original code, which just confused everyone more. My apologies, I thought I was making things easier. . .I understand the problem(s) now. . .Thanks again, Bryan. – Chris Kavanagh Mar 19 '15 at 07:59
  • One last thing, I know you're a TKinter expert (from all your posts), so i was wondering if you could give me some advice on the proper way to code this? Obviously there's got to be a way to catch errors like this, and I''m not doing it the right way. Any suggestions would be appreciated greatly – Chris Kavanagh Mar 19 '15 at 08:10