Instantiate MySQL DB class - python

I would like to write a class that would contain all my MySQL operations.
Right now, I cant even get the class instantiated.
Traceback (most recent call last):
File "./compare.py", line 71, in <module>
main()
File "./compare.py", line 67, in main
db = Table.mysqlconnect()
TypeError: unbound method mysqlconnect() must be called with Table instance as first argument (got nothing instead)
code:
import MySQLdb
class Table(object):
""" Using Databases """
def __init__(self, db, name ):
self.db = db
self.name = name
self.cur = self.db.cursor()
def mysqlconnect():
conn = MySQLdb.connect (host = "mysql.blah.com",
user = "user",
passwd = "password",
db = "database")
cursor = conn.cursor ()
cursor.execute ("SELECT VERSION()")
row = cursor.fetchone ()
print "server version:", row[0]
cursor.close ()
conn.close ()
def main():
db = Table.mysqlconnect()
pass
if __name__ == '__main__':
main()

You should read those docs, but what you're looking for is:
db = Table()
db.mysqlconnect()
Short explanation: mysqlconnect is a instance method on your Table class.
Long explanation: Table is an abstract concept right now -- you've told the Python interpreter about it and what it should do, but you haven't actually made one yet. It's like the blueprint, if you will, for your class. Before you use it, or use any method defined as part of it* you'll need to actually "build it" first.
This is what you do when you do: db = Table() This tells the Python interpreter that I've got this variable called db now and I want it to be an instance of Table(). Now that you've got your instance, you can call the instance method (since the instance method only works on an instance) and get your result.
*There are things called class methods that you can use without instantiating the class first, but you'll see that when you read the documentation.

The error is exactly right:
TypeError: unbound method mysqlconnect() must be called with Table instance as first argument (got nothing instead)
In other words, you are using a class name (Table) and not an instance of the class Table.
Read these examples, they do exactly what you want. If you want to read up on classes and objects, the Python manual chapter on this is excellent.

Also, mysqlconnect needs to use self to access the class attributes. For example:
import MySQLdb
class Table:
def __init__(self, host, user, passwd, name)
self.db = MySQLdb.connect (host = host,
user = user,
passwd = passwd,
db = name)
self.cursor = self.db.cursor()
def mysqlconnect(self):
self.cursor.execute ("SELECT VERSION()")
row = cursor.fetchone()
print "server version:", row[0]
self.cursor.close ()
self.db.close ()
# Main must be outside the table class
def main():
tableInstance = Table("mysql.blah.com", "user", "password", "database")
tableInstance.mysqlconnect()
if __name__ == '__main__':
main()

Related

Python: Calling a function from class B in class A returns AttributeError

I am new to Python and coding in general and after watching a four hour lecture, I decided to give writing a small functional application a shot.
In my application I am working with a tkinter GUI and a SQlite3 database. ClassA handles the GUI and functions that the GUI calls, while ClassB handles the database.
To insert some data the user has entered into the GUI I have defined the function add() in ClassA. The function simply calls method .insert from ClassB and then passes the data the GUI has gathered as a parameter into the .insert function in ClassB. Or so I thought..
Whenever I try I get the following traceback:
self.cur.execute("INSERT TO example(column1, column2, column3, column4) VALUES (?, ?, ?, ?)",
AttributeError: 'ClassA' object has no attribute 'cur'
Here's the code I am trying to run:
class ClassA:
def __init__(self, master):
self.master = master
master.title = ("Example Program")
...
# REST OF GUI
def add(self):
ClassB.insert(self, self.entry_1.get())
class ClassB:
def __init_(self):
self.con = sqlite3.connect("example.db")
self.cur = self.con.cursor()
self.cur.execute("CREATE TABLE IF NOT EXISTS example (row text)")
self.con.commit()
def insert(self, data):
self.cur.execute("INSERT INTO TABLE example (row) VALUES (?)", (data))
self.con.commit()
On the first line of ClassA.add, don't pass in self. self is passed in by python as the object the method was called on. If you have my_object.method(), the instance of my_object would be passed into my_object method as self. An example
Something else that I'm noticing is that calling ClassB.insert should not work since
in order to use a class's methods, you need to instantiate an instance. For e.g.:
b = ClassB()
b.insert(a1, a2,a3, a4)

Is python GC guaranteed to be a ref counting garbage collector

For my repository class, I am creating an connection to the database when a function needs it (if None). I am using the __del__ to close the connection if not None. The object is guaranteed to be short lived.
I realized I am dependent on Ref counting to close the connection as soon as possible. I need the connection to be a field in the class because I am doing a select for update. The default python gc I read does ref counting at least the CPython implementation. But the pypy implementation has many different types of garbage collectors. Is it okay to rely on ref counting gc to close the connection as soon as possible? Or should I write a separate function and ensure that the connection is closed using a try finally block?
The class only consists of the connection field and two functions one to lock the table in database and select values and the other to update them.
This is my current implementation:
class ReserveRepository:
def __init__(self):
self.conn = None
def select(self):
cur = None
try:
self.conn = psycopg2.connect(config.DATABASE_URL)
cur = self.conn.cursor()
cur.execute('lock table sometable in ACCESS EXCLUSIVE mode')
cur.execute('select id from sometable where condition')
return list(map(lambda x: x[0]))
finally:
if cur is not None:
cur.close()
def update_quantities(self, id):
cur = None
try:
if self.conn is None:
self.conn = psycopg2.connect(config.DATABASE_URL)
cur = self.conn.cursor()
cur.execute('update sometable set column1 = value where id = %s', (id,))
self.conn.commit()
return reservation_id
finally:
if cur is not None:
cur.close()
def __del__(self):
if self.conn is not None:
self.conn.close()
self.conn = None
tldr; The answer is no. Pypy does not implement reference counting as far as I understand. To demonstrate this, I made this simple program:
class Test:
def __init__(self):
print('__init__')
def __del__(self):
print('__del__')
t = Test()
print(t)
The output on CPython 3.8.2 is:
__init__
<__main__.Test object at 0x7f6e3bc13dc0>
__del__
However, the output on Pypy 7.3.1 (Python 3.6.9) is
__init__
<__main__.Test object at 0x00007f58325dfe88>
The __del__ function is not called.
Even on CPython, the answer is no. If the object is attached to a long-lived cached instance, __del__ will never be called. A better design pattern is to use a context manager for your object and do clean up at __exit__:
with make_a_connection(parameter) as conn:
useconn(conn)

python test delete from mongo

I have a problem with mongo. When I ended my automation tests, I need trash all data and object which I created. I create a script. In this script I delete a rows from a few table. But when I start this, This class doesn't start, where is my problem?
In consol I haven't any message, zero value.
from pymongo import MongoClient
class deleteMongo():
def deleteFirst(self):
client = MongoClient('databaseaddress')
db = client.TableData
db.Employe.delete_one({"name": "EmployeOne"})
def deleteSecond(self):
client = MongoClient('databaseaddress')
db = client.PersonData
db.Person.delete_one({"name": "PersonOne"})
def deleteThird(self):
client = MongoClient('databaseaddress')
db = client.AutoData
db.Auto.delete_one({"name": "AutoThird"})
If I am understanding your question correctly, you are trying to run the script above and it's not doing anything?
If this is your complete module, you are not calling the class at all, but defining the class object.
also the parenthesis in class deleteMongo(): are redundant in a class, since it always inherits the object. On the current setup of this class object, you should use def instead, or setup your class to initialize shared objects of the class.
Based on your current code, try this:
from pymongo import MongoClient
class deleteMongo:
def __init__(self, databaseAddress):
# if the databseAddress is always the same, you can remove it from the arguments
# and hardcode it here
self.client = MongoClient(databaseAddress)
def deleteFirst(self):
db = self.client.TableData
db.Employe.delete_one({"name": "EmployeOne"})
def deleteSecond(self):
db = self.client.PersonData
db.Person.delete_one({"name": "PersonOne"})
def deleteThird(self):
db = self.client.AutoData
db.Auto.delete_one({"name": "AutoThird"})
and then when you need to call one of the class functions, call it like this:
deleteMongo(databaseAddress='someaddress').deleteFirst()

Dealiing with instance in class when parameters changed - Python

I'm learning how to program and handle Python classes and have found this problem.
I have this files (example.py) and inside this file I'm importing two classes.
example.py
..
from class_folder.automobile import Class_Automobile
from class_folder.catalog import Class_Catalog
class_automobile = Class_Automobile()
class_catalog = Class_Catalog()
..
Inside automobile.py have this code:
from pdo_mysql import Pdo_Mysql
class Class_Automobile(object):
"""
Description
"""
__pdo_mysql_intern = None
def __init__(self):
self.__pdo_mysql_intern = Pdo_Mysql('host', 'user', 'password', 'db')
## End def init
def Validate_Make(self, make_search):
query_make = 'make_text = %s '
result = self.__pdo_mysql_intern.select('make', query_make, 'id_make', make=make_search)
return result
## End function Validate Make
And inside the catalog.py file has this information:
from pdo_mysql import Pdo_Mysql
class Class_Catalog(object):
"""
Description
"""
__pdo_mysql_intern = None
def __init__(self):
self.__pdo_mysql_intern = Pdo_Mysql('host', 'user', 'password', 'db2')
## End def init
And finally the pdo_mysql.py file has this:
import MySQLdb, sys
class Pdo_Mysql(object):
"""
Description
"""
__instance = None
__host = None
__user = None
__password = None
__database = None
__session = None
__connection = None
def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = super(Pdo_Mysql, cls).__new__(cls,*args,**kwargs)
return cls.__instance
## End __new__
def __init__(self, host='localhost', user='root', password='', database=''):
self.__host = host
self.__user = user
self.__password = password
self.__database = database
## __init__
def _open(self):
try:
cnx = MySQLdb.connect(self.__host, self.__user, self.__password, self.__database)
self.__connection = cnx
self.__session = cnx.cursor()
except MySQLdb.Error as e:
print "Error %d: %s" % (e.args[0],e.args[1])
## End function open
def _close(self):
self.__session.close()
self.__connection.close()
## End function close
def select(self, table, where=None, *args, **kwargs):
result = None
query = 'SELECT '
keys = args
values = tuple(kwargs.values())
l = len(keys) - 1
for i, key in enumerate(keys):
query += "`"+key+"`"
if i < l:
query += ","
## End for keys
query += 'FROM %s' % table
if where:
query += " WHERE %s" % where
## End if where
self._open()
self.__session.execute(query, values)
result = [item[0] for item in self.__session.fetchall()]
self._close()
return result
## End function select
When I run the example.py I have this problem, somehow when I call this
validate_information = class_automobile.Validate_Make(make)
I obtain this response
File "/Library/Python/2.7/site-packages/MySQLdb/cursors.py", line 205, in execute
self.errorhandler(self, exc, value)
File "/Library/Python/2.7/site-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
raise errorclass, errorvalue
_mysql_exceptions.ProgrammingError: (1146, "Table 'db2.make' doesn't exist")
This problem it is because the db from Class_Catalog is mixing when I'm calling the Class_Automobile.
It is solved when I put the Class_Catalog before the Class_Automobile but I want to know how to solve properlly.
Update:
Thanks to Adam Smith found the problem. I just had to change this file pdo_mysql.py:
class Pdo_Mysql(object):
# all your existing definitions
def __new__(cls, *args, **kwargs):
**if not cls.__instance or not cls.__database:**
cls.__instance = super(Pdo_Mysql, cls).__new__(cls,*args,**kwargs)
return cls.__instance
Here is what's happening. When you call class_automobile = Class_Automobile() a new Class_Automobile instance is created and the __init__ method is called on that instance. Inside __init__ you're calling Pdo_Mysql('host', 'user', 'password', 'db'). Pdo_Mysql.__new__ is invoked with Pdo_Mysql as the cls variable and ('host', 'user', 'password', 'db') as the args variable. Because this is the first time a Pdo_Mysql object has been requested the class variable __instance is None so your super call is run and the result is set on the Pdo_Mysql.__instance variable. This is a class level assignment. You then return this instance. If you look at the Python docs you'll see that :
If __new__() returns an instance of cls, then the new instance’s __init__() method will be invoked
So now you've got the instance of Pdo_Mysql from the super __new__ call and you return it. The __init__ method will be called on it with the args and kwargs sent to __new__. In __init__ you set the various connection attributes.
All is fine at this point. If you were to make calls to your class_automobile instance they would work as you expected. Let's look at what happens when you make your next object, the instance of Class_Catalog.
It begins in the same way as Class_Automobile until we get to the self. __pdo_mysql_intern = Pdo_Mysql call. This time when Pdo_Mysql.__new__ is invoked the cls.__instance variable is set to an instance of Pdo_Mysql so you don't make a new one. BUT you still return this instance from __new__ so the __init__ method is still run on this instance but with new args and kwargs. The instance variables are set to the new values. So now the instances class_automobile and class_catalog have a __pdo_mysql_intern variable pointing to the same instance and that instance has __host, __user, __password, and __database set to whatever the last arguments were used to initialize an instance.
Now on to one possible solution...
It appears you're trying to do some kind of connection sharing so you don't open a new connection for each object that connects to a database. However you're only allowing one instance of Pdo_Mysql to ever exist. This means you cannot connect to more than one database and only as a single user.
You need a pool of connections (Pdo_Mysql objects) to choose from so that when you want to connect to another db or as a different user you can do so. In your Pdo_Mysql class you could change the __instance = None variable to __instances = {}. Use the args as the key to this dictionary and change the __new__ method to look up the instance using the args. Note that this ignores the kwargs, I leave it to you to figure out how to include them (hint, you cannot use mutable objects as keys in dictionaries). You'll also want to add a __initialized variable to the Pdo_Mysql class. Default it to False and set it to True in __init__. This was you can make sure you only run __init__ on each instance once.
Going a step further I would suggest you not use __new__ to create singletons but instead create a helper method in pdo_mysql.py called get_conenction or something. Have it take the same 4 arguments and it can check it's pool of instances for any that match and either return an existing connection or make a new one.

Why can't I see the data from my model in this Pyside form while I did map them together?

I got a toy phonebook application used to learn PySide:
import sys
import os
from PySide import QtCore, QtGui, QtSql
import phonebook_ui
CURRENT_FILE = os.path.abspath(__file__)
CURRENT_DIR = os.path.dirname(CURRENT_FILE)
DB_PATH = os.path.join(CURRENT_DIR, 'db.sqlite')
class PhoneBook(QtGui.QMainWindow, phonebook_ui.Ui_MainWindow):
def __init__(self):
super(PhoneBook, self).__init__()
self.setupUi(self)
self.db = self.create_connection()
# this is where the two tables should me linked automatically
self.model = QtSql.QSqlRelationalTableModel(self)
self.model.setTable('person')
self.model.setEditStrategy(QtSql.QSqlRelationalTableModel.OnManualSubmit)
foreign_key = self.model.fieldIndex('service')
self.model.setRelation(foreign_key,
QtSql.QSqlRelation("service", "id", "nom"))
self.model.select()
self.relation_model = self.model.relationModel(foreign_key)
self.edit_service.setModel(self.relation_model)
self.edit_service.setModelColumn(self.relation_model.fieldIndex("name"))
self.listing.setModel(self.model)
self.listing.setColumnHidden(0, True)
self.listing.setSelectionMode(QtGui.QAbstractItemView.SelectionMode.SingleSelection)
# this is where the model data should be automatically injected in the
# central table
self.mapper = QtGui.QDataWidgetMapper(self)
self.mapper.setModel(self.model)
self.mapper.setItemDelegate(QtSql.QSqlRelationalDelegate(self))
self.mapper.addMapping(self.edit_name, self.model.fieldIndex('name'))
self.mapper.addMapping(self.edit_phone, self.model.fieldIndex('phone'))
self.mapper.addMapping(self.edit_service, foreign_key)
self.listing.selectionChanged = self.on_selection_changed
def on_selection_changed(self, selected, deselected):
range = selected[0]
index = range.indexes()[0]
self.mapper.setCurrentIndex(index.row())
#classmethod
def create_connection(cls, bdd=DB_PATH):
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName(bdd)
db.open()
query = QtSql.QSqlQuery()
query.exec_(u"create table person(id int primary key, name varchar(20), "
u"phone varchar(20), service varchar(20))")
db.commit()
return db
def close(self):
super(PhoneBook, self).close()
self.db.close()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
phonebook = PhoneBook()
phonebook.show()
sys.exit(app.exec_())
Here is a screenshot:
My tables look like this:
I managed to make it work once with no service field, but now that I added it, I introduced the notion of QSqlRelationalTableModel and failed to make it work.
What's it's supposed to do:
fill the big white central table with a listing of people from the DB (fails, despite the model and mapper being setup and the db containing 2 entries)
on click on any of the central table rows, fill the bottom form with the row data
allow edit and save.
The DB is filled up with data and is readable from my tests in the python shell.
The code throw no errors, but nothing happens: the windows is displayed with nothing in there.
Can you point my mistakes here ?
I'll be happy to provide any additional information you need
In code you have service varchar(20)), but in screenshot, service is a separate table (as it should be). But I see nom instead name on screenshot (QtSql.QSqlRelation("service", "id", "name")).
So, if you'll check your database schema and actualize it with code, it should work.
#classmethod
def create_connection(cls, bdd=DB_PATH):
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName(bdd)
db.open()
query = QtSql.QSqlQuery()
query.exec_(u"create table person(id int primary key, name varchar(20), "
u"phone varchar(20), service int)")
query.exec_(u"create table service(id int primary key, name varchar(20))")
db.commit()
return db # don't forget to return it, as you use it in close
P.S. I have not checked "edit" and "add" logic.

Categories

Resources