We're working on a text-based game (MUD) and have hit this roadblock.
The code:
class RoomMove():
def __init__(self):
self.room = 1
self.name = 'Lorinth'
self.moveRooms()
self.updateRoom()
[extra code doesn't matter]
def updateRoom(self):
global c
room = str(self.room)
room = (room)
print room
while room > 0:
c.execute("""SELECT * FROM RoomPlayers where ID=?""", room)
spaceCheck = c.fetchone()
print spaceCheck
counter = 1
if spaceCheck[counter] not in ('', None):
counter += 1
else:
room = self.room
name = self.name
c.execute("""UPDATE RoomPlayers SET '1'=? WHERE ID=?""", (name, room))
conn.commit()
it throws back this error:
c.execute("""SELECT * FROM RoomPlayers where ID=?""", room)
ValueError: parameters are of unsupported type
EDITS: I've tried it with (room, ) ..no difference in the error though.
Any ideas?
Thanks!
As you can read in the documentation:
fetchone()
Fetches the next row of a query result set, returning a single sequence, or None when no more data is available.
If there is no more data c.fetchone() assigns None to the spaceCheck variable, hence TypeError when you try to access spaceCheck[counter].
Try something like that:
if spaceCheck is not None:
counter += 1
UPDATE
You should replace
room = (room)
with:
room = (room, )
Another update
Assuming you create table like below:
sqlite> CREATE TABLE RoomPlayers (ID numeric, player_name VARCHAR);
Your code could like similar to this:
class RoomMove():
def __init__(self, room, name):
self.room = room
self.name = name
self.con = None
self.db_path = "./foo.sqlite"
def updateRoom(self):
try:
# Globals are evil. Don't use them
self.con = sqlite3.connect(self.db_path)
c = con.cursor()
# comparing tuple or string to number dosen't make sense. Compare original number
# You should check if room is valid inside constructor
# while room > 0 would probably lead to indefinite loop
if rooom > 0:
#You don't have to cast room number to string
c.execute("""SELECT id FROM RoomPlayers where ID=?""", (self.room, ))
spaceCheck = c.fetchone()
# Your counter takes only two values so you can do it like that
counter = 2 if spaceCheck is not None else 1
if counter == 1:
# Don't name columns '1' - it is really bad idea
# Like above - coneverting room and name to string is pointlees
c.execute("""UPDATE RoomPlayers SET 'player_name'=? WHERE ID=?""", (self.name, self.room))
self.con.commit()
finally:
# No matter what happens close connection
if self.con is not None:
self.con.close()
What fixed the problem shown above:
sqlite3 doesn't read integers, just strings. (not actually true)
So I made the integer be read as a string in the else loop.
here:
else:
room = self.room
name = self.name
c.execute("""UPDATE RoomPlayers SET '1'=? WHERE ID=?""", (name, room))
room is being passed to c.execute as an integer, the solution?
else:
room = str(self.room)
....
Took forever to see that!
Related
I have created a class that takes name,id number and salary for each object. inside the class there are functions for adding or deduction of the salary and showing the status for each employee:
class emp():
def __init__(self,name,id_num,salary):
self.name=name
self.id=id_num
self.s=salary
def bounus(self,bon):
self.s+=bon
print(" the empolyee %s got a raise of %s"%(self.name,bon))
def ded(self,d):
self.s-=d
print(" the empolyee %s got a deduction of %s"%(self.name,d))
def show(self):
s="the employee {} with id number {} has a salary of {}".format(self.name,self.id,self.s)
print(s)
so I wanted to create a number of objects of my chioce using "range" function in the "for" loop as the following:
for i in range(1,3) :
o=str(input("Enter the employees number %s name\n"%i))
p=input("Enter his\her id number\n")
q=input("Enter his\her salary\n")
ai=emp(o,p,q)
ai.show()
in that way, it loops through 1 and 2 creating objects a1 and a2 and it worked but when I try to show them outside the loop as the following:
a1.show()
it says,a1 is undefined although I could show them inside the loop , how can I store the objects so I can show or apply functions on them after looping .thanks
The i in ai does not get processed as a seperated variable, it just becomes one whole ai.
Instead, you should make a list a, which you can access with a[i].
a = []
for i in range(2) : # Slight change to start at i=0
o=str(input("Enter the employees number %s name\n"%i))
p=input("Enter his\her id number\n")
q=input("Enter his\her salary\n")
a.append(emp(o,p,q))
a[i].show()
Selcuk identified your issue, but here is a code snippet based on your code that may help you conceptualize his advice:
new_employees = []
for i in range(1,3):
name = input("Enter the employees number %s name\n" %i)
id_num = input("Enter his\her id number\n")
salary = input("Enter his\her salary\n")
employee = emp(name, id_num, salary)
employee.show()
new_employees.append(employee)
At the end of the loop you will now have a list of new employees that you can do other things with. So, per your comment assume you want to deduct $25 from the salary of on the employee with the employee id of 5. You could something like this if you didn't want to get fancy:
target_employee = None
for employee in new_employees:
if employee.id == 5:
target_employee = employee
break
if target_employee:
target_employee.ded(25)
Here is another way that auto-creates a name for each employee the way you intended and stores that name and the employee object in a dictionary. Each employee can then be called by his name from outside the loop with full access to all the class methods. Also class names should always be capitalized. Object names are in lower case:
class Emp():
def __init__(self, name, id_num, salary):
self.name = name
self.id = id_num
self.s = salary
def bonus(self, bon):
self.s += bon
print("The empolyee %s got a raise of %s" % (self.name, bon))
def ded(self, d):
self.s -= d
print("The empolyee %s got a deduction of %s" % (self.name, d))
def show(self):
s = "The employee {} with id number {} has a salary of {}".format(self.name, self.id, self.s)
print(s)
employees = {}
for i in range(1, 3):
o = input("Enter the employees number %s name\n" % i)
p = input("Enter his\her id number\n")
q = int(input("Enter his\her salary\n"))
emp = Emp(o, p, q)
name = "a" + str(i)
employees[name] = emp
employees["a1"].show()
employees["a2"].bonus(500)
employees["a2"].ded(200)
employees["a2"].show()
The first mistake you have done is declaring the class inside the for loop. The scope of the object is limited to the for loop and will be destroyed after the loop and moreover you cannot store all the values wrt to the loop as every time a loop is run the new object will be invoked destroying all the previous one hence us the list to append them and try
Lets say I have a function that query some table company.workers, for example (pseudo code):
def sql_query(dept, prof, hireDate):
q = """SELECT *
FROM company.workers
WHERE department = {0}
AND profession = {1}
AND hire_date > {2}""".format(dept, prof, hireDate)
cur.execute(q)
return cur
What if I want to allow the user the query only on dept and prof, with hireDate being optional? This is the solution I came up with:
def sql_query(dept, prof, *args):
if args:
q = """SELECT *
FROM company.workers
WHERE department = {0}
AND profession = {1}
AND hire_date > {2}""".format(dept, prof, args[0])
else:
q = """SELECT *
FROM company.workers
WHERE department = {0}
AND profession = {1} """.format(dept, prof)
cur.execute(q)
return cur
#the function could be called as so:
sql_query('20', 'Engineer', (2017-12-10))
However I think this is subotimal. What if I want to allow several optional coloumns to query? If I make it two, I have 4 options to handle, which is a lot of else-if blocks to make. Is there a more efficient/elegant solution?
You don't say what SQL DBMS you're using, but here's a block of SQL Server-style code that accepts three variables:
#dept
#prof
#hireDate
The WHERE clause always uses #dept and #prof, and only uses #hireDate if it is not null.
SELECT
*
FROM
company.workers
WHERE
department = #dept
AND
profession = #prof
AND
(
(
#hireDate IS NOT NULL AND hire_date = #hireDate
)
OR #hireDate IS NULL
)
You could then add as many other option variables as needed using this same style, instead of writing separate SQL statements for each combination.
Similar question here. You're right, you don't really want to be manually generating the statement. You could improve your current code by making it more dynamic using a dictionary:
def sql_query(**params):
q = "SELECT * FROM company.workers"
count=0
for i in non_require_param:
if count==0:
q += " WHERE {0} = {1} ".format(i, params[i])
else:
q += " AND {0} = {1} ".format(i, params[i])
count += 1
cur.execute(q)
return cur
Also, sqlite's execute cursor is something to look into. It is cleaner than formatting the statement yourself and handles the datatype conversion:
who = "Yeltsin"
age = 72
cur.execute("""select * from company.workers
where name_last=:name_last and age=:age""",
{"name_last": who, "age": age})
I am trying to loop through my One2Many records to avoid duplication.
class sales_target(models.Model):
_name = 'sales.target'
_description = 'Sales Target'
name = fields.Char(string='Name',required=True)
from_date = fields.Date(string='From Date',required=True)
to_date = fields.Date(string='To Date',required=True)
sales_team = fields.Many2one('crm.team',required=True)
sales_record_ids = fields.One2many('sales.target.record','sales_target_rec_id',string='Sales Record')
#api.one
def check_duplication(self,result):
count = 0
if self.sales_record_ids:
for record in self.sales_record_ids:
if result.id == record.sales_person_p_id:
count = 1
if count == 0:
self.write({'sales_record_ids':[(0,0,{'sales_person':result.name})]})
#api.one
def get_sales_person(self):
for res in self.sales_team.member_ids:
self.check_duplication(res)
The other class is as:
class sales_target_record(models.Model):
_name = 'sales.target.record'
sales_target_rec_id = fields.Many2one("sales.target")
sales_person = fields.Char(string='Sales Person',readonly=True,required=True)
sales_person_p_id = fields.Char(compute='get_value',store=True)
#api.onchange('sales_person')
#api.depends('sales_person')
def get_value(self):
res = self.env['res.partner'].search([('name','=',self.sales_person)])
self.sales_person_p_id = res[0].id
Now when I am hitting the button i still have duplicate records. However I tried to compare with name and things work good but I cannot compare with names since its not correct because names can be same but id cannot. That function was as:
#api.one
def check_duplication(self,result):
count = 0
if self.sales_record_ids:
for record in self.sales_record_ids:
if result.name == record.sales_person:
count = 1
if count == 0:
self.write({'sales_record_ids':[(0,0,{'sales_person':result.name})]})
Hope for guidance on this.
Can you try like this
#api.multi
def check_duplication(self,result):
if self.sales_record_ids:
for record in self.sales_record_ids:
if not result.name == record.sales_person:
self.write({'sales_record_ids':[(0,0,{'sales_person':result.name})]})
Concluding from the fact that for name it works properly, something might be wrong with your if condition.
sales_person_p_id is of type char, however you seem to compare it with an integer: result.id.
Have you made sure that both objects in your if condition are of the same type?
Try to make sales_person_p_id an integer field (e.g. via sales_person_p_id = fields.Integer(compute='get_value',store=True) or do some kind of type casting before comparing the objects.
I'm new to Python. I've just created a sqlite3 db wrapper class to handle the database interaction in my application. My question is how do I return success/failure messages from my db wrapper class methods, addRow, updRow (which add and update rows) to the invoking program?
Here's the class code I cobbled together so far:-
class dbManager(object):
def __init__(self, db):
self.conn = lite.connect(db)
self.conn.execute('pragma foreign_keys = on')
self.conn.execute('pragma synchronous=off')
self.conn.commit()
self.cur = self.conn.cursor()
def query(self, arg):
self.cur.execute(arg)
self.conn.commit()
return self.cur
def addRow(self, tablename, data):
"""
Insert data into a table. The data does not have to be escaped.
"""
global actInserts
# Create a new cursor
# tc = self.conn.cursor() # opened at init
tablelist = ""
valueholder = ""
valuelist = []
for key, value in data.items():
if len(tablelist) > 0:
tablelist += ', '
valueholder += ', '
# Add to table column list
tablelist += key
# Add a holder
valueholder += '?'
# build the insert values
valuelist.append(value)
# Perform and commit the insert
try:
dbResponse = self.cur.execute("INSERT INTO " + tablename + " (" + tablelist + ") VALUES (" + valueholder + ");", valuelist)
actInserts += 1
except lite.Error, e:
dbResponse = 'Sqlite Error NP: ' + e.args[0]
print 'Sqlite Error NP: ' + e.args[0]
return dbResponse
def closeConnection (self):
self.conn.commit()
self.conn.close()
def __del__(self):
self.conn.close()
I think there are 2 ways you can do this. You can:
Change the return type of you function to something like a tuple of (error_code, SQL_results), or
Throw an exception if the query fails (or don't catch the one you already are handling) and leave the exception handling logic to the user (calling program).
I think option 2 is the better way.
Also, I think your query building would be more clear (or at least more concise) using something like the following (assumes that data is a dictionary)
tablelist = ','.join(list(data.viewkeys()))
valueholder = ','.join(['?' for i in data.viewkeys()])
valuelist = list(data.viewvalues())
Tried the following, where "objectname" contains a string name, to be assigned on creation of an object.
for record in result:
objectname = 'Customer' + str(record[0])
print objectname
customername = str(record[1])
objectname = Customer(customername)
Where Customer is a class.
In my test, this loop runs twice printing "objectname" as Customer1 and Customer2, yet creates 2 objects, but the objects are called "objectname" (it overwrites each loop), opposed to the 2 unique objects Customer1 or Customer2.
Its simply not assigning strings(customer1,2) inside the variable, but purely the variables name.
I've tried assigning strings to the object name, but that gives a syntax error
Surely this must be done all the time, thanks for your help in advance.
Instead of using a new variable for each customer you could store your object in a Python dictionary:
d = dict()
for record in result:
objectname = 'Customer' + str(record[0])
customername = str(record[1])
d[objectname] = Customer(customername)
print d
An example of objects stored in dictionaries
I just could'nt help my self writting some code (more than I set out to do). It's like addictive. Anyway, I would'nt use objects for this kind of work. I probably would use a sqlite database (could be saved in memory if you want). But this piece of code show you (hopefully) how you can use dictionaries to save objects with customer data in:
# Initiate customer dictionary
customers = dict()
class Customer:
def __init__(self, fname, lname):
self.fname = fname
self.lname = lname
self.address = None
self.zip = None
self.state = None
self.city = None
self.phone = None
def add_address(self, address, zp, state, city):
self.address = address
self.zip = zp
self.state = state
self.city = city
def add_phone(self, number):
self.phone = number
# Observe that these functions are not belonging to the class.
def _print_layout(object):
print object.fname, object.lname
print '==========================='
print 'ADDRESS:'
print object.address
print object.zip
print object.state
print object.city
print '\nPHONE:'
print object.phone
print '\n'
def print_customer(customer_name):
_print_layout(customers[customer_name])
def print_customers():
for customer_name in customers.iterkeys():
_print_layout(customers[customer_name])
if __name__ == '__main__':
# Add some customers to dictionary:
customers['Steve'] = Customer('Steve', 'Jobs')
customers['Niclas'] = Customer('Niclas', 'Nilsson')
# Add some more data
customers['Niclas'].add_address('Some road', '12312', 'WeDon\'tHaveStates', 'Hultsfred')
customers['Steve'].add_phone('123-543 234')
# Search one customer and print him
print 'Here are one customer searched:'
print 'ooooooooooooooooooooooooooooooo'
print_customer('Niclas')
# Print all the customers nicely
print '\n\nHere are all customers'
print 'oooooooooooooooooooooo'
print_customers()
It is generally not that useful to have dynamically generated variable names. I would definitely suggest something like Niclas' answer instead, but if you know this is what you want here is how you can do it:
for record in result:
objectname = 'Customer' + str(record[0])
print objectname
customername = str(record[1])
exec '%s = Customer(%r)' % (customername, customername)
This will result in the variables Customer1 and Customer2 being added to the innermost scope, exactly like if you had executed the following lines:
Customer1 = Customer('Customer1')
Customer2 = Customer('Customer2')
When doing it this way you need to make sure that customername is a valid Python identifier.
What you need is a dictionary:
customers = {}
for record in result:
objectname = 'Customer' + str(record[0])
customers[customername] = Customer(str(record[1])) #assignment to dictionary