Python Mysql class to call a stored procedure - python

I am very new. All of my experience is on the DB side so I am lost on the Python side of things. That said I am trying to create a class that I can use to execute stored procedures. I am using Python 3.4.3. I found a mysql class on github and simplified/modified it to make a proc call and it is not working.
mysqlquery.py
import mysql.connector, sys
from collections import OrderedDict
class MysqlPython(object):
__host = None
__user = None
__password = None
__database = None
__procname = None
__inputvals = None
def __init__(self, host='localhost', user='root', password='', database=''):
self.__host = host
self.__user = user
self.__password = password
self.__database = database
## End def __init__
def __open(self):
cnx = mysql.connector.connect(self.__host, self.__user, self.__password, self.__database)
self.__connection = cnx
self.__session = cnx.cursor()
## End def __open
def __close(self):
self.__session.close()
self.__connection.close()
## End def __close
def proc(self,procname,inputvals):
self.__open()
self.__session.callproc(procname, inputvals)
## End for proc
## End class
test.py
from mysqlquery import MysqlPython
connect_mysql = MysqlPython()
result = connect_mysql.proc ('insertlink','1,www.test.com')
I get this error
TypeError: __init__() takes 1 positional argument but 5 were given
Looking at my init, it take 5 args as it should. Not sure why I am getting this. Again, I am very new so it could be a simple problem.
Thanks for any help.
G

mysql.connector.connect() takes named arguments, not positional arguments, and you're missing the names.
cnx = mysql.connector.connect(host=self.__host, user=self.__user, password=self.__password, database=self.__database)

Related

Psycopg3 returns None randomly

So i have a flask app, that connects to a Postgres12 DB using a user that just has select privileges. Often times i see my apis return 400 error, and api does this when the sql query executed returns None.
I have built a small wrapper class over execute and executemany function for some error handling.
import time
from functools import wraps
import psycopg
from psycopg import InterfaceError, OperationalError
logger = logging.getLogger(__name__)
def retry(fn):
#wraps(fn)
def wrapper(*args, **kw):
cls = args[0]
exec = None
for x in range(cls._reconnectTries):
try:
return fn(*args, **kw)
except (InterfaceError, OperationalError) as e:
logger.warning(f"Database Connection {e} exception type: {type(e)}")
logger.info(f"Idle for {cls._reconnectIdle} seconds")
time.sleep(cls._reconnectIdle)
cls._connect()
exec = e
import sys
logger.exception(f"Exiting the system, {exec} ")
sys.exit(exec)
return wrapper
class Connection:
_reconnectTries = 5
_reconnectIdle = 2
def __init__(self, conn_string):
self._conn_string = conn_string
self.conn = None
self.cursor = None
self._connect()
def _connect(self):
self.conn = psycopg.connect(self._conn_string)
self.conn.autocommit = True
self.cursor = self.conn.cursor()
#retry
def execute(self, **kwargs):
# self.conn.commit()
if "query" in kwargs:
"""
this is done to ensure postgres logs multi line queries sent by client in single line for easier
log collection and debugging.
"""
kwargs["query"] = kwargs["query"].replace("\n", " ")
kwargs["query"] = " ".join(kwargs["query"].split())
return self.cursor.execute(**kwargs)
#retry
def executemany(self, **kwargs):
# self.conn.commit()
return self.cursor.executemany(**kwargs)
For the sake of simplicity, the query code looks somewhat like
store_detail_by_link_query = """
SELECT
json_build_object('id', store.id, 'uuid', store.uuid)
FROM
optimus_store store
WHERE
store.link= %(store_link_or_domain)s and store.is_active = TRUE and store.is_deleted = FALSE;
"""
optimus_connection = Connection(conn_string=CONN_STRING)
params = {
"store_link_or_domain": "dipen28",
}
row = optimus_connection.execute(
query=store_detail_by_link_query,
params=params,
).fetchone()
The problem is, in the same api call if i just do a check that result is None, then rerun the query, at that time the result comes.
I know the data is there in database, this is coming to us in our production system and i am unable to reproduce it at local.
Any help is much appreciated.

Inherited Methods How do I make super methods available?

I have a class (which works [or appears to]) to run an sql query. Code is below. If I inherit from object and do not use super, it works fine.
I am learning about inserting methods from super classes and so I thought that I would make my class header look like this
class database_connector(Connection)
and incorporate a super call in init like this
super().__init__()
However, I get
TypeError: function() argument 1 must be code, not str
I tried
super(database_connector, self).__init__()
after reading some other stuff in StackOverFlow but I now still get
TypeError: function() argument 1 must be code, not str
I am anticipating that this work will allow me to call more methods from pymysql.Connection.
Here is my class
from pymysql import Connection
# set up default values for database connector
class database_connector(Connection):
def __init__(self, host=None, db=None, user=None, passwd=None):
super(database_connector, self).__init__()
if host is None:
self.host = "mysql_host_ip"
else:
self.host = host
if db is None:
self.db = "fred_db"
else:
self.db = db
if user is None:
self.user = "fred"
else:
self.user = user
if passwd is None:
self.passwd = "fredspasswd"
else:
self.passwd = passwd
self.this_database = (Connection(host=self.host,
user=self.user,
passwd=self.passwd,
db=self.db))
self.cur = self.this_database.cursor()
def my_sql_run_this_sql(self, sql_to_run=None):
if sql_to_run is None:
data = self.cur.execute("SELECT * FROM person")
else:
data = self.cur.execute(sql_to_run)
data = []
for row in self.cur.fetchall():
data.append(row)
self.this_database.close()
return data

Calling method to another method in python

I have lots of database queries and I would like to use some methods to not repeat my code. I would like to call methods in other defined methods but it doesn't work
I'm getting such error:
class Main:
File "d.py", line 20, in Main
for word in getUserWords("SELECT users.mail, field_data_field_what_word_are_you_looking_.field_what_word_are_you_looking__value, users.uid FROM users INNER JOIN field_data_field_what_word_are_you_looking_ ON users.uid = field_data_field_what_word_are_you_looking_.entity_id"):
TypeError: getUserWords() takes exactly 2 arguments (1 given)
my code
import MySQLdb as mdb
Class Main:
def connect(self):
con = mdb.connect('***', '*****', '****', '***', charset="utf8", use_unicode=True)
return con
def cursor(self):
cursor = self.connect.cursor()
return cursor()
def getUserWords(self, sql):
self.sql = sql
self.cursor.execute(self.sql)
data = self.cursor.fetchall()
self.connect.commit()
self.connect.close()
return data
for word in getUserWords("SELECT users.mail, field_data_field_what_word_are_you_looking_.field_what_word_are_you_looking__value, users.uid FROM users INNER JOIN field_data_field_what_word_are_you_looking_ ON users.uid = field_data_field_what_word_are_you_looking_.entity_id"):
print word
Simpler example:
class Foo(object):
def __init__(self):
self.foo = "bar"
def function1(self,x):
self.function2(x)
def function2(self,y):
print y
bar = Foo()
bar.function1(3) # calls function1 which in turn calls function2 which prints out 3
bar.function2(4) # calls function 2 directly.
The main takeaway to answer your question:
If you have a class function, it has a first argument which is by convention self. If you call that class function on an instance (as in bar.function2), the self is implicit. If you call that class function from within the class (as when function1 calls function2), you need to do self.functionname, which again implicitly passes the self argument.
First point: instanciate your class and call getUserWords() on your instance:
import MySQLdb as mdb
class Main:
# snip
m = Main()
sql = your_sql_here
for word in m.getUserWords(sql):
print word
Second point: your implementation of Main is flawed.
Class Main:
def connect(self):
# this will open a new connection on each and every call
con = mdb.connect('***', '*****', '****', '***', charset="utf8", use_unicode=True)
return con
def cursor(self):
# this will
# 1. create a new connection on every call - which will
# never be closed since you don't keep a reference
# on it so you can close it
# 2. create a new cursor on every call
cursor = self.connect.cursor()
# and this one will raise a TypeError
# => "'Cursor' object is not callable"
return cursor()
# so I assume your real code is :
return cursor
def getUserWords(self, sql):
# assigning sql to self is totally useless here
self.sql = sql
# so (assuming self.cursor returns the cursor and not
# the call to the cursor), this will:
# - open a new connection
# - create a new cursor
# - execute the sql
# - and discards eveything (cursor and connection)
# without closing them
self.cursor.execute(self.sql)
# now we
# - open a second connection (without closing the first)
# - create a second cursor
# - call .fetchall() on it, which will raise a
# _mysql_exceptions.ProgrammingError
data = self.cursor.fetchall()
# we're not making it until this part because of
# the above error, but if we did, this would:
# - create yet a third connection and call .commit()
# on it - which in this case would mainly be a no-op
# since we have nothing to commit
self.connect.commit()
# and finally create a fourth connection and close it
# immediatly - note that this will be the only one that
# gets closed <g>
self.connect.close()
return data
A fixed version of your code could look something like this:
import MySQLdb as mdb
class Main(object):
def __init__(self, connection_data):
self._connection_data = connection_data.copy()
self._connection_data.update(charset="utf8", use_unicode=True)
self._db = None
#property
def db(self):
if self._db is None:
self._db = mdb.connect(**self._connection_data)
return self._db
def cursor(self):
return self.db.cursor()
def execute(self, sql):
cursor = self.cursor()
cursor.execute(self.sql)
for row in cursor:
yield row
self.db.commit()
cursor.close()
def __del__(self):
try:
self._db.close()
except:
# either it's not set or it's already closed
pass
m = Main(db="***", user="***", passwd="***")
for w in m.getUserWords(your_sql_here):
print w

Python method doesn't run in class

I am new to Python and can't seem to figure out why the .getRow method doesn't run. I created a DBMain class in dbMain.py and I am using pyTest.py to create the DBMain object to run getRow. When I run the debugger in Eclipse and DBMain's constructor does run but but when the getRow method is call nothing happens.
pyTest.py
import dbMain
def main():
db = dbMain.DbMain()
db.getRow()
if __name__ == '__main__':
main()
dbMain.py
##PydevCodeAnalysisIgnore
import pyodbc
class DbMain(object):
cncx = ''
def __init__(self):
cnxn = pyodbc.connect(driver='{SQL Server}',
server='server',
database='database',
uid='name',
pwd='pwd')
def getRow():
cursor = cnxn.cursor()
cursor.execute("select user_id, user_name from users")
row = cursor.fetchone()
return row
You do not return anything from getRow. Maybe you want to include something like
...
return row
Your getRow() method is not bound to the class. The signature for an instance method should look something like getRow(self) - the first parameter is the instance, which is received explicitly (but passed implicitly, when you call someinstance.method()).
To have something functional, you maybe should alter your dbMain to something like this:
##PydevCodeAnalysisIgnore
import pyodbc
class DbMain(object):
def __init__(self):
# make cnxn an attribute of the instance
self.cnxn = pyodbc.connect(driver='{SQL Server}', server='server',
database='database', uid='name', pwd='pwd')
# receive `self` explicitly
def getRow(self):
cursor = self.cnxn.cursor()
cursor.execute("select user_id, user_name from users")
row = cursor.fetchone()
# actually return something
return row
Further reading:
Python: Difference between class and instance attributes

Using var from from function A to function B

On this sample code i want to use the variables on the function db_properties at the function connect_and_query. To accomplish that I choose the return. So, using that strategy the code works perfectly. But, in this example the db.properties files only has 4 variables. That said, if the properties file had 20+ variables, should I continue using return? Or is there a most elegant/cleaner/correct way to do that?
import psycopg2
import sys
from ConfigParser import SafeConfigParser
class Main:
def db_properties(self):
cfgFile='c:\test\db.properties'
parser = SafeConfigParser()
parser.read(cfgFile)
dbHost = parser.get('database','db_host')
dbName = parser.get('database','db_name')
dbUser = parser.get('database','db_login')
dbPass = parser.get('database','db_pass')
return dbHost,dbName,dbUser,dbPass
def connect_and_query(self):
try:
con = None
dbHost=self.db_properties()[0]
dbName=self.db_properties()[1]
dbUser=self.db_properties()[2]
dbPass=self.db_properties()[3]
con = None
qry=("select star from galaxy")
con = psycopg2.connect(host=dbHost,database=dbName, user=dbUser,
password=dbPass)
cur = con.cursor()
cur.execute(qry)
data = cur.fetchall()
for result in data:
qryResult = result[0]
print "the test result is : " +qryResult
except psycopg2.DatabaseError, e:
print 'Error %s' % e
sys.exit(1)
finally:
if con:
con.close()
operation=Main()
operation.connect_and_query()
Im using python 2.7
Regards
If there are a lot of variables, or if you want to easily change the variables being read, return a dictionary.
def db_properties(self, *variables):
cfgFile='c:\test\db.properties'
parser = SafeConfigParser()
parser.read(cfgFile)
return {
variable: parser.get('database', variable) for variable in variables
}
def connect_and_query(self):
try:
con = None
config = self.db_properties(
'db_host',
'db_name',
'db_login',
'db_pass',
)
#or you can use:
# variables = ['db_host','db_name','db_login','db_pass','db_whatever','db_whatever2',...]
# config = self.db_properties(*variables)
#now you can use any variable like: config['db_host']
# ---rest of the function here---
Edit: I refactored the code so you can specify the variables you want to load in the calling function itself.
You certainly don't want to call db_properties() 4 times; just call it once and store the result.
It's also almost certainly better to return a dict rather than a tuple, since as it is the caller needs to know what the method returns in order, rather than just having access to the values by their names. As the number of values getting passed around grows, this gets even harder to maintain.
e.g.:
class Main:
def db_properties(self):
cfgFile='c:\test\db.properties'
parser = SafeConfigParser()
parser.read(cfgFile)
configDict= dict()
configDict['dbHost'] = parser.get('database','db_host')
configDict['dbName'] = parser.get('database','db_name')
configDict['dbUser'] = parser.get('database','db_login')
configDict['dbPass'] = parser.get('database','db_pass')
return configDict
def connect_and_query(self):
try:
con = None
conf = self.db_properties()
con = None
qry=("select star from galaxy")
con = psycopg2.connect(host=conf['dbHost'],database=conf['dbName'],
user=conf['dbUser'],
password=conf['dbPass'])
NB: untested
You could change your db_properties to return a dict:
from functools import partial
# call as db_properties('db_host', 'db_name'...)
def db_properties(self, *args):
parser = SafeConfigParser()
parser.read('config file')
getter = partial(parser.get, 'database')
return dict(zip(args, map(getter, args)))
But otherwise it's probably best to keep the parser as an attribute of the instance, and provide a convenience method...
class whatever(object):
def init(self, *args, **kwargs):
# blah blah blah
cfgFile='c:\test\db.properties'
self._parser = SafeConfigParser()
self._parser.read(cfgFile)
#property
def db_config(self, key):
return self._parser.get('database', key)
Then use con = psycopg2.connect(host=self.db_config('db_host')...)
I'd suggest returning a namedtuple:
from collections import namedtuple
# in db_properties()
return namedtuple("dbconfig", "host name user password")(
parser.get('database','db_host'),
parser.get('database','db_name'),
parser.get('database','db_login'),
parser.get('database','db_pass'),
)
Now you have an object that you can access either by index or by attribute.
config = self.db_properties()
print config[0] # db_host
print config.host # same

Categories

Resources