I am trying to fix an issue I am having but can't seem to find the issue. I need to create a Python class for handling database interactions. So far I have done something like this, but I am getting an AttributeError which I could not resolve.
import sqlite3
class Database:
def __init__(self):
self.file = 'database.db'
self.conn = sqlite3.connect(self.file)
self.cur = self.conn.cursor()
def query(self, query, *args):
try:
self.cur.execute(query, args)
self.conn.commit()
self.conn.close()
except sqlite3.Error as e:
print(e)
I then run the following command:
sql = 'INSERT INTO table (name) VALUES (?);'
Database.query(sql, "ABC")
But get this error message:
AttributeError: 'str' object has no attribute 'cur'
Please help me figure this out :D Thanks
You have to initiate your object before using it:
db = Database()
db.query(sql, "ABC")
Related
I have app for working with database that uses psycopg2. When I try to insert a column with repeating name I got this error:
> self.cursor.execute(str(query))
E psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "deposits_area_name_key"
E DETAIL: Key (area_name)=(test-name) already exists.
dbase-api-server/dbase_api_server/dbase.py:110: UniqueViolation
I use try-except block to catch it and it work. But exception doesn't raise when I run pytests.
import logging
from psycopg2 import connect as connect_to_db
from psycopg2._psycopg import connection as postgres_connection
from psycopg2._psycopg import cursor as db_cursor
from psycopg2.errors import UniqueViolation
from pypika import Query, Table
from dbase_api_server.containers import PostgresConnectionParams
class StorageDBase:
def __init__(self, params: PostgresConnectionParams):
try:
self.__connection = connect_to_db(
host=params.host,
port=params.port,
user=params.user,
password=params.password,
database=params.database
)
except OperationalError as err:
logging.error(err, 'problems with database operation')
raise
#property
def connection(self) -> postgres_connection:
return self.__connection
#property
def cursor(self) -> db_cursor:
return self.__connection.cursor()
def is_success_commit(self) -> bool:
self.connection.commit()
def add_deposit_info(self, area_name: str) -> bool:
table = Table('deposits')
query = str(Query.into(table).columns('area_name').insert(area_name))
try:
self.cursor.execute(query)
return self.is_success_commit()
except UniqueViolation as error:
logging.error(
error,
f'deposit with name {area_name} already exists'
)
return False
Test:
from hamcrest import assert_that, equal_to, is_
from dbase_api_server.dbase import StorageDBase
class TestStorageDBase:
def test_add_deposit_repeating_name(self, up_test_dbase):
area_name = 'test-name'
is_first_added = up_test_dbase.add_deposit_info(area_name)
assert_that(actual_or_assertion=is_first_added, matcher=is_(True))
is_second_added = up_test_dbase.add_deposit_info(area_name)
assert_that(actual_or_assertion=is_second_added, matcher=is_(False))
query_count = f'SELECT COUNT(1) FROM deposits WHERE area_name=\'{area_name}\''
cursor = up_test_dbase.cursor
cursor.execute(query_count)
records_count = cursor.fetchone()[0]
assert_that(actual_or_assertion=records_count, matcher=equal_to(1))
self.remove_records_from_deposits(dbase_adapter=up_test_dbase)
There was a similar question here, but this solutions doesn't help to solve problem.
How I can catch this error?
OperationalError is the wrong error class. See Exceptions:
exception psycopg2.OperationalError
Exception raised for errors that are related to the databaseās operation and not necessarily under the control of the programmer, e.g. an unexpected disconnect occurs, the data source name is not found, a transaction could not be processed, a memory allocation error occurred during processing, etc. It is a subclass of DatabaseError.
The broad exception you want is:
exception psycopg2.DatabaseErrorĀ¶
Exception raised for errors that are related to the database. It is a subclass of Error.
If you want finer grain exceptions you need to look at Errors. For instance the psycopg2.errors.UniqueViolation that shows up in your error message.
So:
try:
<some query>
except psycopg2.errors.UniqueViolation:
<do something>
Problem is in exceptions and logging.There was mistake. I make next fix:
def add_deposit_info(self, area_name: str) -> bool:
table = Table('deposits')
query = str(Query.into(table).columns('area_name').insert(area_name))
try:
self.cursor.execute(query)
return self.is_success_commit()
except UniqueViolation:
logging.error('field(s) has non unique value')
return False
And it`s okay
Instead of using:
import sqlite3
conn = sqlite3.connect(':memory:')
c = conn.cursor()
c.execute(...)
c.close()
would it be possible to use the Pythonic idiom:
with conn.cursor() as c:
c.execute(...)
It doesn't seem to work:
AttributeError: __exit__
Note: it's important to close a cursor because of this.
You can use contextlib.closing:
import sqlite3
from contextlib import closing
conn = sqlite3.connect(':memory:')
with closing(conn.cursor()) as cursor:
cursor.execute(...)
This works because closing(object) automatically calls the close() method of the passed in object after the with block.
A simpler alternative would be to use the connection object with the context manager, as specified in the docs.
with con:
con.execute(...)
If you insist on working with the cursor (because reasons), then why not make your own wrapper class?
class SafeCursor:
def __init__(self, connection):
self.con = connection
def __enter__(self):
self.cursor = self.con.cursor()
return self.cursor
def __exit__(self, typ, value, traceback):
self.cursor.close()
You'll then call your class like this:
with SafeCursor(conn) as c:
c.execute(...)
Adding to sudormrfbin's post. I've recently experienced an issue where an INSERT statement wasn't committing to the database. Turns out I was missing the with context manager for just the Connection object.
Also, it is a good practice to always close the Cursor object as well, as mentioned in this post.
Therefore, use two contextlib.closing() methods, each within a with context manager:
import contextlib
import sqlite3
# Auto-closes the Connection object
with contextlib.closing(sqlite3.connect("path_to_db_file")) as conn:
# Auto-commit to the database
with conn:
# Auto-close the Cursor object
with contextlib.closing(conn.cursor()) as cursor:
# Execute method(s)
cursor.execute(""" SQL statements here """)
I'm trying inherit psycopg2 like this:
import psycopg2
class myp(psycopg):
pass
ii = myp
ii.connect(database = "myDataBase", user = "myUser", password="myPassword")
Then it throws an error:
class myp(psycopg2._psycopg):
TypeError: Error when calling the metaclass bases
module.__init__() takes at most 2 arguments (3 given)
Is it possible to inherit from psycopg2 library?
EDIT:
I want to use different databases, so I just have to change the class MyDatabase. something like a wrapper.
example:
import psycopg2
class MyDatabase(psycopg2):
def connect(self):
#do some stuff
return psycopg2.connect(database = "myDataBase", user = "myUser", password="myPassword")
for mysqldb
import MySQLdb
class MyDatabase(MySQLdb)
def connect(self):
#do some stuff
return psycopg2.connect(database = "myDataBase", user = "myUser", password="myPassword")
and derived class
class MyDataBaseApp(MyDatabase):
def add(self, myObjectClass):
db = MyDatabase()
cn = None
try:
cn = db.connect()
cur = cn.cursor()
cur.execute ("INSERT ...",(myObjectClass.parameter1, myObjectClass.parameter2))
cn.commit()
except MyDatabase.DatabaseError, e:
print e
if cn:
cn.rollback()
finally:
if cn:
cn.close()
but according to the documentation I have to do it another way, suggestions?
Disclaimer: I'm not familiar with psycopg
Update
Seems the documentation recommends to subclass psycopg2.extensions.connection. Then, connect() is a factory function that can still be used to create new connections, but you have to provide your class as a factory, again according to the docs
Full code may have to look more like (untested):
import psycopg2
class myp(psycopg2.extensions.connection):
pass
ii = connect(connection_factory=myp,
database = "myDataBase", user = "myUser", password="myPassword")
Update 2
With the updated approach, you're trying to build new classes with different/divergent interfaces. Often, composition is better than inheritance, see wikipedia and this question.
I have problem with using class instance in Python.
Ive created a new class ora which inherit connect class from cx_Oracle package.
When I try tu run this code I recive information
File "pyt.py", line 12, in myquery
ora.myConnect.cursor()
AttributeError: 'NoneType' object has no attribute 'cursor'
So Python cannote recognize that in ora.myConnect is stored reference to instance.
I dont know what can be reason of this error and what its wrong with code.
from cx_Oracle import connect
class ora(connect):
myConnect = None
def __init__(self,connstr):
ora.myConnect = connect.__init__(self,connstr)
def myquery(self):
ora.myConnect.cursor()
ora.myConnect.cursor.execute("SELECT * FROM table")
ora.myConnect.cursor.close()
connstr = 'user/passwd#host:port/sid'
connection = ora(connstr)
connection.myquery()
connection.close()
EDIT
Ive tried to replace ora to self but still Python dont have access to instance
from cx_Oracle import connect
class ora(connect):
myConnect = None
def __init__(self,connstr):
self.myConnect = connect.__init__(self,connstr)
def myquery(self):
self.myConnect.cursor()
self.myConnect.cursor.execute("SELECT * FROM table")
self.myConnect.cursor.close()
Error:
self.myConnect.cursor()
AttributeError: 'NoneType' object has no attribute 'cursor'
EDIT2
This code works without OOP, for me self.myConnect sholud reference to object instance and this object should contain method cursor()
import cx_oracle
connstr = 'user/passwd#host:port/sid'
connection = cx_oracle.connect(connstr)
cursor = connection.cursor()
cursor.execute("SELECT * FROM table")
cursor.close()
connection.close()
It seems like you want self:
class ora(connect):
myConnect = None
def __init__(self, connstr):
self.myConnect = connect.__init__(self, connstr)
# ...
ora is the name of the class, not the instance.
Update Try the following:
from cx_Oracle import connect
class ora:
myConnect = None
def __init__(self, connstr):
self.myConnect = connect(connstr)
def myquery(self):
self.myConnect.cursor()
self.myConnect.cursor.execute("SELECT * FROM table")
self.myConnect.cursor.close()
Why do you want self.myConnect to refer to the connect instance? That's a complete misunderstanding of OOP. The ora instance is the connect instance. self.cursor is where you find the cursor.
Here's how your code should look:
class ora(connect):
def __init__(self,connstr):
super(ora, self).__init__(connstr)
def myquery(self):
self.cursor.execute("SELECT * FROM table")
self.cursor.close()
In any case, __init__ must never return anything, so setting self.myConnect to the return value will always result in it being bound to None.
I'm trying to come up with SQLiteDB object, and following is the open/close code for it.
Does this work without problem? Am I missing something important?
For close(), I use con.close() and cursor.close(), but I'm wondering if cursor.close() is necessary.
class SQLiteDB(object):
def __init__(self, dbFile, connect = True):
self.dbFile = dbFile
self.con = None
self.cursor = None
if connect:
self.open()
def __del__(self):
if self.con:
self.close()
def open(self):
self.con = sqlite3.connect(self.dbFile)
self.cursor = self.connector.cursor()
return self.con, self.cursor
def close(self):
self.con.close()
self.cursor.close()
self.cursor = None
self.con = None
What happens on Cursor.close() depends on the underlying database implementation. For SQLite it might currently work without closing, but for other implementations or a future SQLite version it might not, so I would recommend to close the Cursor object. You can find further information on Cursor.close() in PEP 249.
Also, there seems to be a typo in your code:
self.connector = sqlite3.connect(self.dbFile)
should probably be
self.con = sqlite3.connect(self.dbFile)
Otherwise your code looks fine to me. Happy coding :) .