Python attribute error when using sqlite cursor from class - python

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

How do I catch a psycopg2.errors.UniqueViolation error in a Python?

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

Is there a "with conn.cursor() as..." way to work with Sqlite?

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 """)

How can I inherit from psycopg2?

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.

Class instance in Python

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.

The open/close function of SQLite implementation

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 :) .

Categories