Should I use a single database connection for my PySIde application? - python

I am writing a GUI application with PySide. It has a few tables that get populated with data from a database, by pushing the corresponding buttons.
I thought of using a single database connection when the application starts and pass conn and cursor as variables in any functions that concern the database:
import sqlite3
database_fullpath = self.get_database_fullpath()
conn = sqlite3.connect(database_fullpath)
cursor = conn.cursor()
def populate_table(self, conn, cursor):
# do something
Maybe, I should connect to the database everytime it's needed:
import sqlite3
def populate_table(self):
database_fullpath = self.get_database_fullpath()
conn = sqlite3.connect(database_fullpath)
cursor = conn.cursor()
# do something
I am not sure which is the right approach and what the advantages of the different methods are.

One approach might be to provide a helper class that reuses the connection if available and reconnects if not:
class SqliteDb(object):
def __init__(self, db_fullpath):
self.db_fullpath = db_fullpath
self.conn = None
def connect(self):
self.conn = sqlite3.connect(self.db_fullpath)
def cursor(self, factory=sqlite3.Cursor):
try:
return self.conn.cursor(factory=factory)
except (AttributeError, sqlite3.ProgrammingError):
self.connect()
return self.conn.cursor(factory=factory)
def close(self):
self.conn.close()
Then in your code:
database_fullpath = self.get_database_fullpath()
sqlite_db = SqliteDb(database_fullpath)
cursor = sqlite_db.cursor()

Related

What is the right way to design Python / SQLite application without repeating connection part?

im trying to develop a simple application where multiple business objects are saved to the sqlite database. I have several business objects for which I want to program save/update/delete methods. For each of the classes and methods within those classes I always create a new connection. For example:
import sqlite3
db = "mydb.db"
class BusinessObject1:
...
def save_to_db(self, db):
conn = sqlite3.connect(db)
cur = conn.cursor()
with conn:
cur.execute("...")
def delete_from_db(self, db):
conn = sqlite3.connect(db)
cur = conn.cursor()
with conn:
cur.execute("...")
class BusinessObject2:
...
def save_to_db(self, db):
conn = sqlite3.connect(db)
cur = conn.cursor()
with conn:
cur.execute("...")
def delete_from_db(self, db):
conn = sqlite3.connect(db)
cur = conn.cursor()
with conn:
cur.execute("...")
It doesn't feel like a good design solution (not DRY). Can someone propose a better design for this? I have around 20 Business objects and 4-8 methods for each and a record table where all these objects are included. Typing conn=sqlite3.connect(db) every time simply cannot be the right way. And if I'll decide to change to MySQL or PostgreSQL I would need to refactor the whole project.
Thanks!
Then you might want to separate the connection handler from the class object or you can do as the comment above suggesting to put the connection inside the class constructor.
If for some reason you can't do both you can still add a staticmethod to your class that will handle the connection. It's not ideal but it's still better than your solution:
#staticmethod
def create_connection(db_file):
conn = None
try:
with sqlite3.connect(db_file) as conn:
return conn
except sqlite3.Error as e:
print("Can't connect to database, error:", e, sep="\n")
return conn
Then you add a parameter to your other methods which will be the returned create_connection method:
def save_to_db(self, connection):
cur = connection.cursor()
cur.execute("...")
This way you can separate your connect object from your class object:
if __name__ == "__main__":
conn = BusinessObject1.create_connection(db)
b1 = BusinessObject1(...)
b1.save_to_db(conn)

How to avoid writing "MySQLdb.connect" again and again?

I wonder if there is a way to avoid writing MySQLdb.connect(host='localhost', user='xyz', password='xyz', db='xyz')
again and again in Python?
Example:
def Add_New_User(self):
self.db = MySQLdb.connect(host='localhost', user='xyz', password='xyz', db='xyz')
self.cur = self.db.cursor()
after two or three lines (when new def is required and a lot of def is required), I need to write all over again the same string many times
def Add_New_User(self):
self.db = MySQLdb.connect(host='localhost', user='xyz', password='xyz', db='xyz')
self.cur = self.db.cursor()
I hope that there is a way to call it in a simple way, like a connection code written and saved in, e.g., MyConString.py then in the new def say, index.py, I would simply call the function MyConString.
Create a function called connection:
def conn(database):
import mysql.connector
connection = mysql.connector.connect(host='localhost',
database=database,
user='root',
password='yourpass')
cursor = connection.cursor(buffered=True)
return connection, cursor
Now, whenever you have to connect in any function do this:
def insert():
connection_data = conn("mydatabase")
connection = connection_data[0]
cursor = connection_data[1]
cursor.execute("show tables;")
connection.commit()
connection.close()
You can even make an entirely new script for functions that are repeatedly used to keep your main file clean.
Then you can just add import connections.py(say, that is your file name which contains conn() function)
# This is your main.py
import connections
def insert():
connection_data = connections.conn("mydatabase")
connection = connection_data[0]
cursor = connection_data[1]
cursor.execute("show tables;")
connection.commit()
connection.close()
Hope this helped!

Python and MySQL - Weak Reference Error when Executing Query

I am new to Python and MySQL and having some trouble with a connection class that I am using to execute queries.
Here is my what I have so far:
class DbConnection:
def __init__(self):
db = mysql.connector.connect(
host=cfg.mysql["host"],
user=cfg.mysql["user"],
passwd=cfg.mysql["passwd"],
database=cfg.mysql["database"]
)
self.cursor = db.cursor()
def query(self, sql):
self.cursor.execute(sql)
return self.cursor.fetchall()
test = DbConnection()
test.query('SELECT * FROM DaycareDogs')
When I try and run this (or any query) I am getting "ReferenceError: weakly-referenced object no longer exists".
I am very new to coding and this is my first real project so am learning on the fly.
Is there something I am missing? I have seen a few other similar problems and did what was recommended but still no luck.
Any advice?
Thanks!
Try making the database connection a property of the class, by also adding self in front of it
class DbConnection:
def __init__(self):
self.db = mysql.connector.connect(
host=cfg.mysql["host"],
user=cfg.mysql["user"],
passwd=cfg.mysql["passwd"],
database=cfg.mysql["database"]
)
self.cursor = self.db.cursor()
def query(self, sql):
self.cursor.execute(sql)
return self.cursor.fetchall()
test = DbConnection()
test.query('SELECT * FROM DaycareDogs')
Since the variable db is in the local scope, when the 'init' constructor ends, the variable will be destroyed, therefore the connection

Mysqldb AttributeError: cursor

I am starting to use the mysqldb module in python and I seem to have some issues with the "standard" way of calling queries.
I understand that the standard way is to create a cursor and then use it to execute queries.
However, when I try to instanciate one, it gives me the following error :
AttributeError: cursor
My Database class looks like :
class Database():
def __init__(self):
server = "localhost"
login = "login"
password = "passws"
database = "DB"
my_conv = { FIELD_TYPE.LONG: int }
self.conn = MySQLdb.connection(user=login, passwd=password, db=database, host=server, conv=my_conv)
self.cursor = self.conn.cursor()
def close(self):
self.conn.close()
def execute(self, query):
self.cursor.execute(query)
return self.cursor.fetchall()
For now I get it working by using the query method, but I feel not using the standard will give me trouble in the future.
Any idea ?
You are using wrong connection constructor.
MySQLdb.Connection instead of MySQLdb.connection should work.

How to enable MySQL client auto re-connect with MySQLdb?

I came across PHP way of doing the trick:
my_bool reconnect = 1;
mysql_options(&mysql, MYSQL_OPT_RECONNECT, &reconnect);
but no luck with MySQLdb (python-mysql).
Can anybody please give a clue? Thanks.
I solved this problem by creating a function that wraps the cursor.execute() method since that's what was throwing the MySQLdb.OperationalError exception. The other example above implies that it is the conn.cursor() method that throws this exception.
import MySQLdb
class DB:
conn = None
def connect(self):
self.conn = MySQLdb.connect()
def query(self, sql):
try:
cursor = self.conn.cursor()
cursor.execute(sql)
except (AttributeError, MySQLdb.OperationalError):
self.connect()
cursor = self.conn.cursor()
cursor.execute(sql)
return cursor
db = DB()
sql = "SELECT * FROM foo"
cur = db.query(sql)
# wait a long time for the Mysql connection to timeout
cur = db.query(sql)
# still works
I had problems with the proposed solution because it didn't catch the exception. I am not sure why.
I have solved the problem with the ping(True) statement which I think is neater:
import MySQLdb
con=MySQLdb.Connect()
con.ping(True)
cur=con.cursor()
Got it from here: http://www.neotitans.com/resources/python/mysql-python-connection-error-2006.html
If you are using ubuntu Linux there was a patch added to the python-mysql package that added the ability to set that same MYSQL_OPT_RECONNECT option (see here). I have not tried it though.
Unfortunately, the patch was later removed due to a conflict with autoconnect and transations (described here).
The comments from that page say:
1.2.2-7 Published in intrepid-release on 2008-06-19
python-mysqldb (1.2.2-7) unstable; urgency=low
[ Sandro Tosi ]
* debian/control
- list items lines in description starts with 2 space, to avoid reformat
on webpages (Closes: #480341)
[ Bernd Zeimetz ]
* debian/patches/02_reconnect.dpatch:
- Dropping patch:
Comment in Storm which explains the problem:
# Here is another sad story about bad transactional behavior. MySQL
# offers a feature to automatically reconnect dropped connections.
# What sounds like a dream, is actually a nightmare for anyone who
# is dealing with transactions. When a reconnection happens, the
# currently running transaction is transparently rolled back, and
# everything that was being done is lost, without notice. Not only
# that, but the connection may be put back in AUTOCOMMIT mode, even
# when that's not the default MySQLdb behavior. The MySQL developers
# quickly understood that this is a terrible idea, and removed the
# behavior in MySQL 5.0.3. Unfortunately, Debian and Ubuntu still
# have a patch right now which *reenables* that behavior by default
# even past version 5.0.3.
I needed a solution that works similarly to Garret's, but for cursor.execute(), as I want to let MySQLdb handle all escaping duties for me. The wrapper module ended up looking like this (usage below):
#!/usr/bin/env python
import MySQLdb
class DisconnectSafeCursor(object):
db = None
cursor = None
def __init__(self, db, cursor):
self.db = db
self.cursor = cursor
def close(self):
self.cursor.close()
def execute(self, *args, **kwargs):
try:
return self.cursor.execute(*args, **kwargs)
except MySQLdb.OperationalError:
self.db.reconnect()
self.cursor = self.db.cursor()
return self.cursor.execute(*args, **kwargs)
def fetchone(self):
return self.cursor.fetchone()
def fetchall(self):
return self.cursor.fetchall()
class DisconnectSafeConnection(object):
connect_args = None
connect_kwargs = None
conn = None
def __init__(self, *args, **kwargs):
self.connect_args = args
self.connect_kwargs = kwargs
self.reconnect()
def reconnect(self):
self.conn = MySQLdb.connect(*self.connect_args, **self.connect_kwargs)
def cursor(self, *args, **kwargs):
cur = self.conn.cursor(*args, **kwargs)
return DisconnectSafeCursor(self, cur)
def commit(self):
self.conn.commit()
def rollback(self):
self.conn.rollback()
disconnectSafeConnect = DisconnectSafeConnection
Using it is trivial, only the initial connect differs. Extend the classes with wrapper methods as per your MySQLdb needs.
import mydb
db = mydb.disconnectSafeConnect()
# ... use as a regular MySQLdb.connections.Connection object
cursor = db.cursor()
# no more "2006: MySQL server has gone away" exceptions now
cursor.execute("SELECT * FROM foo WHERE bar=%s", ("baz",))
you can separate the commit and the close for the connection...that's not cute but it does it.
class SqlManager(object):
"""
Class that handle the database operation
"""
def __init__(self,server, database, username, pswd):
self.server = server
self.dataBase = database
self.userID = username
self.password = pswd
def Close_Transation(self):
"""
Commit the SQL Query
"""
try:
self.conn.commit()
except Sql.Error, e:
print "-- reading SQL Error %d: %s" % (e.args[0], e.args[1])
def Close_db(self):
try:
self.conn.close()
except Sql.Error, e:
print "-- reading SQL Error %d: %s" % (e.args[0], e.args[1])
def __del__(self):
print "close connection with database.."
self.conn.close()
I had a similar problem with MySQL and Python, and the solution that worked for me was to upgrade MySQL to 5.0.27 (on Fedora Core 6; your system may work fine with a different version).
I tried a lot of other things, including patching the Python libraries, but upgrading the database was a lot easier and (I think) a better decision.
In addition to Liviu Chircu solution ... add the following method to DisconnectSafeCursor:
def __getattr__(self, name):
return getattr(self.cursor, name)
and the original cursor properties like "lastrowid" will keep working.
You other bet it to work around dropped connections yourself with code.
One way to do it would be the following:
import MySQLdb
class DB:
conn = None
def connect(self):
self.conn = MySQLdb.connect()
def cursor(self):
try:
return self.conn.cursor()
except (AttributeError, MySQLdb.OperationalError):
self.connect()
return self.conn.cursor()
db = DB()
cur = db.cursor()
# wait a long time for the Mysql connection to timeout
cur = db.cursor()
# still works

Categories

Resources