I have a database class and this class contains a method used to insert records. This is how the method looks:
def insertRecord(self, **kwargs):
if 'table' not in kwargs.keys():
raise Exception('The table keyword is required')
table = kwargs['table']
del kwargs['table']
query_fields = kwargs.keys()
pg_fields = []
for field in query_fields:
pg_fields.append('%(' + field + ')s')
query_field_string = ', '.join(query_fields)
query_pg_string = ', '.join(pg_fields)
self.cur.execute('INSERT INTO ' + table + '(' +
query_field_string + ') VALUES (' + query_pg_string + ')',
kwargs
)
self.conn.commit()
The method accepts variable arguments list so the user can use this method to insert entries in any table. Bassically, the method is constructing a query string of the form INSERT INTO <table>(<field1>, <field2>...) VALUES (%(field1)s, %(field2)s...), since the execute method accepts as the second argument a dictionary of the form <field>: <value> all the strings of the form %(field)s will be replaced with the corresponding value.
Basically, the method works fine, but I don't know how should I test it. Should I make a test database and see if the values passed to it are in the database after calling it? How would you write tests for such a method?
Refactor the code to format the SQL command, then test that. In this way it's much simpler -- pass in args, get a formatted string and a dictionary back. No mocking needed.
source
# python -m unittest insertrec
import unittest
def formatInsert(table, **kwargs):
assert table
query_fields = kwargs.keys()
pg_fields = []
for field in query_fields:
pg_fields.append('%(' + field + ')s')
query_field_string = ', '.join(query_fields)
query_pg_string = ', '.join(pg_fields)
return (
'INSERT INTO ' + table + '(' +
query_field_string + ') VALUES (' + query_pg_string + ')',
kwargs
)
class TestInsert(unittest.TestCase):
def test_err_notable(self):
self.assertRaises(AssertionError, formatInsert, None)
def test_insert1(self):
self.assertEquals(
formatInsert('mytab', beer='tasty'),
('INSERT INTO mytab(beer) VALUES (%(beer)s)',
{'beer': 'tasty'}
),
)
Related
i'm trying to get fields from another model then do some operation on them, there is no problem with logic but I'm getting this error when the methods runs
psycopg2.DataError: invalid input syntax for type double precision: "1.007 t"
these all what I have done
class uom_custom(models.Model):
_inherit = 'product.template'
uom_qty = fields.Char(store=True,compute='get_qty')
#api.depends('qty_available')
def get_qty(self):
uoms=self.env['uom.uom'].search(['&',('category_id', '=', self.uom_id.category_id.id),('show_qty','=',True)])
if uoms.uom_type == 'bigger':
self.uom_qty= str(str(self.qty_available / uoms.factor_inv) + ' ' + uoms.name)
elif self.uom_type =='smaller':
self.uom_qty= str(self.qty_available * uoms.factor_inv) + ' ' + uoms.name
else:
self.uom_qty= str(self.qty_available) + ' ' + uoms.name
return self.uom_qty
so how can I display the value of mathematic operation and uom name beside it
Thanks in advance
The error states that the column in database is defined as double precision. Are you sure you've restarted Odoo and updated your module?
And there are some common mistakes in your compute method. Firstly and i can't repeat it often enough: try to stick to the Odoo naming guideline and name it compute_uom_qty. Secondly without a special decorator a compute method can and will be called with more than one record, so loop on that records. Thirdly: you search for uom.uom which can lead to more than one record, too. So either limit the search to one record or/and implement a check if something was found. uoms.name can lead to SingletonError. And at last: you don't have to return anything in compute methods.
#api.depends('qty_available')
def compute_uom_qty(self):
for record in self:
uoms = self.env['uom.uom'].search(
[('category_id', '=', record.uom_id.category_id.id),
('show_qty','=',True)], limit=1)
if uoms.uom_type == 'bigger':
qty = record.qty_available / uoms.factor_inv
record.uom_qty = "{} {}".format(qty, uoms.name)
elif uoms.uom_type =='smaller':
qty = record.qty_available * uoms.factor_inv
record.uom_qty = "{} {}".format(qty, uoms.name)
else:
record.uom_qty = "{} {}".format(record.qty_available, uoms.name)
I'm having some issues where whenever I make a call to one of my classes methods it's requiring me to specifically send the containing class with the call, where I would expect it to already know about it self. I'm sure this is user error but can not track it down.
I've referenced python - self - required positional argument but i think i've got that covered.
class SpeechEngine():
def __init__(self):
self.conn = sqlite3.connect('../twbot.db')
self.c = self.conn.cursor()
#staticmethod
def choose(choice):
num_choices = len(choice)
selection = random.randrange(0, num_choices)
return selection
def initial_contact_msg(self, userId, screenName):
hello = self.c.execute("SELECT text, id FROM speechConstructs WHERE type='salutation'").fetchall()
tagline = self.c.execute("SELECT text, id FROM speechConstructs WHERE type='tagline'").fetchall()
c1 = self.choose(hello)
c2 = self.choose(tagline)
msg_string = str(hello[c1][0]) + ' #' + screenName + ' ' + tagline[c2][0]
# print(msg_string) # For Testing Only
# print(hello[c1][1]) # For Testing Only
return msg_string
And then I would expect to call
SpeechEngine.initial_contact_msg(0, 'somename')
But that returns the following
missing 1 required positional argument: 'self'
Where as if i do it implicitly
SpeechEngine.initial_contact_msg(SpeechEngine, 0, 'somename')
It returns the expected results no questions asked.
I should also point out the same happens when i would assign it as follows.
test = SpeechEngine
test.initial_contact_msg(0, 'somename')
Since initial_contact_msg is a method, you need to call it from an instance, not the Type. Your last attempt is almost right. To instantiate it, you need to do the following:
test = SpeechEngine()
test.initial_contact_msg(0, 'sometime')
"SpeechEngine" is the type class. When you create a new instance you need to call it like a function. This is similar to using the "new" keyword in other languages.
When you have a static method, this can be called directly from the Type object:
SpeechEngine.choose()
You can read more in the Python Documentation.
I have a list variable, with a string in it. For some reason, it's printing like this:
print('Value = ' + str(var))
outputs:
Value = [\'123456789\']
It seems as if those are the escaped quote ticks added by the str function, but I don't know how to get rid of them.
EDIT: Sorry for the confusion. Here is the more complete back-story:
I'm writing a chat analytics app. Here's my Message class:
class Message:
def __init__(self, name, text, likers):
self.name = name
self.text = text
self.likers = likers
def __str__(self):
return 'Message: ' + self.name + ' said "' + self.text + '", liked by ' + str(self.likers)
This is where messages are stored:
message = data['response']['messages'][i]
user_id = message['user_id']
name = message['name'] # Sender
text = re.sub(r'\W+', ' ', str(message['text']))
likers = message['favorited_by']
msg = Message(name, text, likers)
users[user_id].messages.append(msg)
Above, data is a JSON returned from the chat server. i is my variable to loop through the messages (received in blocks of 20). users is a dictionary mapping user ID's to User objects (my other class). Since each message can be liked by multiple users, likers is an array of user ID's.
The problem arises when I'm trying to print messages. Test code:
print([str(x) for x in users[user_names['MyUser']].messages])
(user_names is a dictionary that maps usernames to user ID's)
That code prints:
['Message: MyUser said "all of you", liked by [\'25843278\']', 'Message: MyUser said "why?", liked by [\'25843278\']']
In your code, likers is a list of string.
likers = ['25843278']
If you use str, Python will convert this list into a string, like this:
text = str(likers)
print(repr(text))
# -> "['25843278']"
So, your __str__ method will return a string with single quote.
Further, you create a list of messages:
[str(x) for x in users[user_names['MyUser']].messages]
Python will print this list. It will convert each item to its string representation. So it will escape the single quotes of each likers.
To avoid this, you can convert the list of likers into a string yourself, like this:
def __str__(self):
return ('Message: ' + self.name + ' said "' + self.text + '",
liked by ' + ', '.join(self.likers))
The keyword arguments are idx, name and
passwd. to update the record with index 3 to the name ’Brian’, the method is called as
update(idx=3, name=’Brian’). The method returns False if the parameter idx is absent. also the password may be present and the record with the index is updated with the given values
for the fields.
I've tried something like this, but get a str object not callable error, I've attempted to look at other scripts here, but I keep getting errors.
def update(self, **kwargs):
''' keyword arguments are idx, name and password.
For example, to update the record with index 3 to
the name ’Brian’, the method is called as
update(idx=3, name=’Brian’). The method returns
False if the parameter idx is absent. In addition
to name, also the passwd may be present and the record
with the index is updated with the given values
for the fields. The method update returns True if the
updates succeeded, or False otherwise.'''
if 'idx' in kwargs:
query = 'UPDATE players set name = ?, password = ? WHERE idx = ?' (kwargs['name'], kwargs['password'],kwargs['idx'])
self.cr.execute(query)
self.db.commit()
print('records updated')
return True
else:
print('records failed to update')
return False
You can't put the argument with the query like you've done:
query = 'UPDATE players set name = ?, password = ? WHERE idx = ?' (kwargs['name'], kwargs['password'],kwargs['idx'])
Python will think you're trying to call that string literal like it was a function.
Instead, pass the args when you execute the query, because the execute() method is what actually stuffs your values into the SQL statement.
query = 'UPDATE players set name = ?, password = ? WHERE idx = ?'
self.cr.execute(query, (kwargs['name'], kwargs['password'], kwargs['idx']))
A better approach is to use named placeholders; then you can just pass in kwargs instead of having to dig out the fields you want:
query = 'UPDATE players set name = :name, password = :password WHERE idx = :idx'
self.cr.execute(query, kwargs)
To answer the question from your comment, you can dynamically create the query based on the fields for which you have values by iterating over the dictionary:
assert "idx" in kwargs # idx is a required field
query = "UPDATE players SET"
for field in kwargs:
if field != "idx":
query += " {f} = :{f},".format(f=field)
query = query.strip(",") # strip off unneeded comma after last field
query += " WHERE idx = :idx"
self.cr.execute(query, kwargs)
Or as a single statement:
query = "UPDATE players SET " + ", ".join(f + " = :" + f
for f in kwargs if f != "idx") + " WHERE idx = :idx"
I have a class Dbcrud() that I will outline below.
I want to take several parameters from a method: db_select() one being selected_fields which would be a list of fields.
Im having trouble forming my method db_select() to allow multiple fields to be defined for selected_fields.
Can someone help?
Thank you
UPDATED
class DbCrud:
query_stmt_list = ['SELECT','INSERT','UPDATE','DELETE','FROM','WHERE']
def __init__(self):
self.query_stmt_list = DbCrud.query_stmt_list
self.query_stmt_list = query_stmt_list
def set_db_settings(self, host, username, passwd, database):
self.host = host
self.username = username
self.passwd = passwd
self.database = database
db = pymysql.connect(host=host, user=username, passwd=passwd, db=database)
return db
def db_select(self, selected_fields, table, where_field):
self.selected_fields = selected_fields
self.table = table
self.where_field = where_field
try:
with db.cursor() as cursor:
sql_tld_id_query = self.query_stmt_list[0] + selected_fields* + self.query_stmt_list[4] + table + self.query_stmt_list[5] + where_field + '=' + %s
cursor.execute(sql_tld_id_query, (self.site_search_url,))
tld_id_value = cursor.fetchone()
except:
db.rollback()
pass
You have few issues here:
1.
You need that row at the init method (as this is the c'tor of the class)
Also as query_stmt_list is a static member, you should access him with the class name as prefix.
def __init__(self):
self.query_stmt_list = DbCrud.query_stmt_list
2.
You can't define a function param with selected_fields[], it's a syntax error, you can pass to selected_fields whatever you like.
def db_select(self, selected_fields, table, where_field):
3.
When you try to use the variable query_stmt_list (at the following line of code i've attached), do you mean you want the class member or the instance member?
If instance you should change it to self.query_stmt_list
If the class member, you should change it to DbCrud.query_stmt_list
sql_tld_id_query = query_stmt_list[0] + selected_fields* + query_stmt_list[4] + table + query_stmt_list[5] + where_field + '=' + %s
Also, in order to loop though the selected_fields you could do:
query_stmt_list[0] + ", ".join(item for item in self.selected_fields) + query_stmt_list[4] ...
You can always expect selected_fields to be a list so you can use ', '.join:
def db_select(self, selected_fields, table, where_field):
query = 'select {selected_fields} {table} where {where_field} = 1'
query = query.format(selected_fields=', '.join(selected_fields),
table=table, where_field=where_field)
.
.
obj.db_select(['col_a'], 'table_a', 'where_field_a')
Note that this is vulnerable to SQL injection, but so is your original code (if it wasn't for the syntax errors it currently has).