25

I am making a Python project where I have to seek and retreive data from a database.
I tried making a class, in which I declare the connection and do my queries, here is moreless what I have so far.

import MySQLdb
dbc =("localhost","root","1234","users")
class sql:
    db = MySQLdb.connect(dbc[0],dbc[1],dbc[2],dbc[3])
    cursor = db.cursor()

    def query(self,sql):
        sql.cursor.execute(sql)
        return sql.cursor.fetchone()

    def rows(self):
        return sql.cursor.rowcount

sqlI = sql()
print(sqlI.query("SELECT `current_points` FROM `users` WHERE `nick` = 'username';"))

So, the main problem is that the variable db and cursor are not callable from other def's/functions from the same Class. What I'd like to get, is a polished query, where I can make queries and retreive it's content. This would summarize my code, therefore I should do.

Marshall
  • 392
  • 1
  • 4
  • 11
  • 1
    Just think about this line... `sql.cursor.execute(sql)`... Which `sql` variable is going to be used to get the cursor? The parameter, not the class – OneCricketeer Jun 28 '16 at 12:37
  • You should remove the unnecessary tags from your question. It's either python 2.7 or 3, or unrelated to a version. – RvdK Jun 28 '16 at 12:46
  • @cricket_007 The submited one, since it's a function – Marshall Jun 29 '16 at 08:50

4 Answers4

86

I usually use psycopg2 / postgres, but this is the basic DB class that I often use, with Python's SQLite as an example:

import sqlite3

class Database:
    def __init__(self, name):
        self._conn = sqlite3.connect(name)
        self._cursor = self._conn.cursor()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

    @property
    def connection(self):
        return self._conn

    @property
    def cursor(self):
        return self._cursor

    def commit(self):
        self.connection.commit()

    def close(self, commit=True):
        if commit:
            self.commit()
        self.connection.close()

    def execute(self, sql, params=None):
        self.cursor.execute(sql, params or ())

    def fetchall(self):
        return self.cursor.fetchall()

    def fetchone(self):
        return self.cursor.fetchone()

    def query(self, sql, params=None):
        self.cursor.execute(sql, params or ())
        return self.fetchall()

This will let you use the Database class either normally like db = Database('db_file.sqlite) or in a with statement:

with Database('db_file.sqlite') as db:
    # do stuff

and the connection will automatically commit and close when the with statement exits.

Then, you can encapsulate specific queries that you do often in methods and make them easy to access. For example, if you're dealing with transaction records, you could have a method to get them by date:

def transactions_by_date(self, date):
    sql = "SELECT * FROM transactions WHERE transaction_date = ?"
    return self.query(sql, (date,))

Here's some sample code where we create a table, add some data, and then read it back out:

with Database('my_db.sqlite') as db:
    db.execute('CREATE TABLE comments(pkey INTEGER PRIMARY KEY AUTOINCREMENT, username VARCHAR, comment_body VARCHAR, date_posted TIMESTAMP)')
    db.execute('INSERT INTO comments (username, comment_body, date_posted) VALUES (?, ?, current_date)', ('tom', 'this is a comment'))
    comments = db.query('SELECT * FROM comments')
    print(comments)

I hope this helps!

carusot42
  • 1,171
  • 10
  • 17
  • 1
    Gorgeous answer! Thank you so much, I sitll not understanding the `__enter__` statment, when you `return` the DBase, what would happen. – Marshall Jun 29 '16 at 08:58
  • 6
    The `__enter__` and `__exit__` "magic methods" let a class use the `with` statement. It's basically saying return an instantiated version of this class when it's used in a `with DBase() as db:` context. – carusot42 Jun 29 '16 at 13:48
  • More info here: http://stackoverflow.com/questions/1984325/explaining-pythons-enter-and-exit – carusot42 Jun 29 '16 at 13:49
  • Should'nt you close also the cursor in DBase.__exit__() ? if not, why? thanks! – rain_ Nov 17 '17 at 08:38
  • In `pyscopg2` at least (the postgres client for Python), the cursor will close automatically when you close the connection, so I don't bother to do it explicitly. It's very likely that this will be taken care of for you. – carusot42 Jan 13 '18 at 02:56
  • @carusot42 Do you believe is it a good idea close both, cursor and connection in \_\_del\_\_() magic method?.. I don't want to use \_\_enter\_\_() and \_\_exit\_\_() methods. Thanks – Carmoreno Apr 23 '20 at 20:36
  • No. You should add a `close()` method which might optionally perform a commit and closes both resources. You should only use `del` or implement `__del__` when you are in a very low-memory environment and cannot depend on the garbage collector to take care of items which are no longer needed in a timely manner, which is rare, and it's very unlikely that your database connection qualifies. I updated the answer to include an example. – carusot42 Apr 23 '20 at 21:35
  • what if I create a simple function to create/connect to db and create a cursor to run my sql instead of creating this class , context manager will take care of everything. Here we are only calling internal methods only. – dev Apr 26 '20 at 19:04
  • You can do that too if you want to. An advantage of using a class like this in a larger app, however, is that you can contain the SQL and database logic inside the object. This is commonly called encapsulation. Then if you need to change what type of database you're connecting to, or details of your database schema changes, then you only need to make changes in the class and all of your code which uses it will still work with no change, which in a large application, could save you a *lot* of work. – carusot42 Apr 26 '20 at 21:23
  • I think you should add a commit() to your example to show where this method should be used and not used. – Nikki Dec 15 '21 at 23:13
  • Hello @carusot42! On your example, could you add a suggestion of method/code that check if the connection is alive and try to reconnect if not? I am struggling to design the method and a easy way to do it. I want my class to be "connection error safe" – FábioRB Aug 09 '23 at 20:57
14

That's not how you write classes in Python. You need to define your connection and cursor inside the __init__ method, and refer to them via self.

class sql:

    dbc = ("localhost","root","1234","users")

    def __init__(self):
        db = MySQLdb.connect(*self.dbc)
        self.cursor = db.cursor()

    def query(self,sql):
        self.cursor.execute(sql)
        return self.cursor.fetchone()

    def rows(self):
        return self.cursor.rowcount
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
2

You can use constructor for the connection. When the object of class will created the constructor will invoke automatically.

import MySQLdb

class Connection:
    def __init__(self):
        self.con=MySQLdb.connect("127.0.0.1","root","","db_name",3306)
        self.cmd=self.con.cursor()
obj=Connection()
0
from config import Config
import MySQLdb

class Connection:
    def __init__(self):
        self.db=MySQLdb.connect(
            Config.DATABASE_CONFIG['server'],
            Config.DATABASE_CONFIG['user'],
            Config.DATABASE_CONFIG['password'],
            Config.DATABASE_CONFIG['name']
            )
        self.db.autocommit(True)
        self.db.set_character_set('utf8mb4')
        self.cur=self.db.cursor()

EXAMPLE CONFIG CLASS(config.py):

class Config(object):

  DATABASE_CONFIG = {
          'server': 'localhost',
          'user': 'dbuser',
          'password': 'password',
          'name': 'dbname',
          }