I'm using MySQLdb module in Python 2.7 with a MariaDB database. I want to know how would be the proper way of using a database across multiple modules. I have this example in which I try to insert elements from another function into the database. I'm getting OperationalError: (1046, 'No database selected')
Edit: Here is the corrected code ( 1 , 2 ). Thanks. Know I would like a working example like this one with dependency injection.
This is the module in which I want to handle the database.
# test.py
import MySQLdb
import traceback
import test2 as insert_module
from warnings import filterwarnings
# Ignore MySQL `table already exists` warnings
filterwarnings ('ignore', category = MySQLdb.Warning)
# Create database object to keep it open as global variable
class MyDB (object):
connection = None
cursor = None
# Open database connection
def connect (self):
self.connection = MySQLdb.connect ()
self.cursor = self.connection.cursor ()
def execute (self, sql, *args):
try:
self.cursor.execute (sql, *args)
self.connection.commit ()
except MySQLdb.Error, e:
print (traceback.format_exc())
self.connection.rollback ()
def query (self, sql, *args):
try:
self.execute (sql, *args)
# Reopen database connection
except ( AttributeError, MySQLdb.OperationalError ):
self.connect ()
self.execute (sql, *args)
def disconnect (self):
self.connection.close ()
db = MyDB ()
def createDatabase ():
db.query ("CREATE DATABASE IF NOT EXISTS Product")
db.query ("USE Product")
sql = """CREATE TABLE IF NOT EXISTS `ProductColor` (
`ProductID` VARCHAR(20) PRIMARY KEY,
`ColorID` VARCHAR(20)
)"""
db.query (sql)
def insertProductColor (*args):
sql = """INSERT INTO `ProductColor` VALUE (%s, %s)"""
db.query (sql, args)
def main ():
createDatabase ()
insert_module.processListOfProductColors ()
if __name__ == "__main__":
main ()
db.disconnect ()
This is the module from which I want to insert the products.
#test2.py
import test as db_module
def processListOfProductColors ():
db_module.insertProductColor ("cup", "blue")
Mostly unrelated but:
# Create database object to keep it open as global variable
class MyDB (object):
connection = None
cursor = None
Those class attributes are useless - define them as instance attributes instead:
def __init__(self):
self.connection = None
self.cursor = None
and while we're at it: DO NOT reuse the same cursor for all your queries. Else you're in for trouble when trying to execute another query while iterating over the results of a first one (and let's not talk multithreading...)
# Open database connection
def connect (self):
self.connection = MySQLdb.connect ()
You should certainly either check if you already have an opened connection, and if yes either close it first or just skip the MySQLdb.connect()
call.
Also and FWIW, you have to pass connection infos (username, password, database name etc) to the MySQLdb.connect()
call. Your OperationalError comes from not passing a database name here.
self.cursor = self.connection.cursor ()
cf above : this will only get you troubles. Use a new cursor each time, that's the intended use of the dbapi.
def execute (self, sql, *args):
try:
self.cursor.execute (sql, *args)
cf above
self.connection.commit ()
You should give the caller a way to NOT commit after each call - he may want to execute more than one statement in a single transaction (actually that's part of the point of having transactions...)
except MySQLdb.Error, e:
you want:
except MySQLdb.Error as e:
print (traceback.format_exc())
Setup a logger (python's logging
module) and call logger.exception(...)
instead.
self.connection.rollback ()
and ? Pretend it's ok ??? You want to re-raise the exception here obviously.
def query (self, sql, *args):
try:
self.execute (sql, *args)
Are you sure you want to commit transactions for read queries ???
# Reopen database connection
except ( AttributeError, MySQLdb.OperationalError ):
self.connect ()
self.execute (sql, *args)
Err... If the point was to lazily handle the connection then you failed. Look at python's support for computed attributes instead ( property
etc).
def disconnect (self):
self.connection.close ()
More generally: trying to wrap the connection part in something that will gracefully (and eventually lazily) handle connection issues might be a good idea (but it's not that easy either...), but by all mean let the caller handle the cursor and transaction manually. A solution here could be to make execute()
and query()
context managers that close the cursor and commit (or rollback) the transaction. Also remember that a bad exception handling is worst than no exception handling...
ideally, you should pass your DB object through dependency injection into the insert_module
- since it doesn't look like you've set up your code like that, you could ( as a quick hack , not the best solution) do:
insert_module.processListOfProductColors (self.db)
and then inside processListOfProductColors(db)
use db.insertProductColor
- as opposed to db_module.insert...
again, this is not the best solution without some refactoring, from what i can tell
update
here is a general example for dependency injection, which you can adapt for your own use:
# note that each class can be in a different module (file),
# just make sure you import
class db_class(object):
def __init__(self):
self.db = create_db()
@staticmethod
def create_db():
db = # create the db
return db
class main(object):
db = db_class()
first_class = first_class(db) # dependency injection part 1
first_class.whatever()
# you can pass db to however many classes you want now
class first_class(object):
def __init__(self, db):
self._db = db # dependency injection part 2
def whatever(self):
# do whatever with self._db
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.