Come gestire le connessioni al database in un modulo di libreria Python

16

Ho creato una libreria in Python che contiene funzioni per accedere a un database. Questa è una libreria wrapper attorno a un database di applicazioni di terze parti, scritta a causa del fatto che l'applicazione di terze parti non offre un'API decente. Inizialmente, ho lasciato che ogni funzione aprisse una connessione al database per la durata della chiamata della funzione, che era OK, finché la logica del mio programma non ha usato chiamate nidificate alle funzioni in cui avrei chiamato una funzione particolare qualche migliaio di volte. Questo non era molto performante. La creazione di profili ha mostrato che l'overhead era nell'impostazione della connessione al database, una volta per chiamata di funzione. Così ho spostato la connessione aperta dalla / e funzione / e al modulo stesso, in modo che la connessione al database fosse aperta quando il modulo della libreria è stato importato. Questo mi ha dato una performance accettabile.

Ora ho due domande a riguardo. In primo luogo, devo preoccuparmi che non stia più chiudendo esplicitamente la connessione al database e come potrei farlo esplicitamente con questo set-up? In secondo luogo, ciò che ho fatto si colloca ovunque vicino al regno delle buone pratiche e come potrei altrimenti avvicinarmi a questo?

    
posta leancz 05.06.2013 - 11:58
fonte

4 risposte

24

Dipende molto dalla libreria che stai utilizzando. Alcuni potrebbero chiudere la connessione da soli (Nota: ho controllato la libreria sqlite3 integrata, e non è così). Python chiamerà un distruttore quando un oggetto esce dall'ambito e queste librerie potrebbero implementare un distruttore che chiude le connessioni con garbo.

Tuttavia, potrebbe non essere il caso! Consiglierei, come altri hanno nei commenti, di avvolgerlo in un oggetto.

class MyDB(object):

    def __init__(self):
        self._db_connection = db_module.connect('host', 'user', 'password', 'db')
        self._db_cur = self._db_connection.cursor()

    def query(self, query, params):
        return self._db_cur.execute(query, params)

    def __del__(self):
        self._db_connection.close()

Questo istanzerà la tua connessione al database all'inizio e la chiuderà quando il posto dove è stato istanziato l'oggetto non rientra nell'ambito. Nota: se istanziate l'oggetto questo a livello di modulo, esso persisterà per l'intera applicazione. A meno che ciò non sia previsto, suggerirei di separare le funzioni del database dalle funzioni non di database.

Fortunatamente, python ha standardizzato l'API del database , quindi funzionerà con tutti gli utenti conformi DB per te:)

    
risposta data 05.06.2013 - 13:15
fonte
3

durante la gestione delle connessioni al database ci sono due cose di cui preoccuparsi:

  1. prevenire le connessioni multiple di istanze, lasciando che ogni funzione apra una connessione al database è considerata una cattiva pratica, dando il numero limitato di sessioni del database, si esauriranno le sessioni; almeno la tua soluzione non si ridimensiona, invece, usa il modello singleton, la tua classe verrebbe istanziata una sola volta, per ulteriori informazioni su questo modello vedi link

  2. chiudendo la connessione all'uscita dall'app, diciamo che non l'hai fatto, e che hai almeno una dozzina di istanze in cui l'app è stata eseguita, inizialmente tutto sarebbe andato a posto, ma il database si sarebbe esaurito sessioni, e l'unica soluzione sarebbe quella di riavviare il server del database, che non è una buona cosa per un'app live quindi utilizzare la stessa connessione quando possibile.

per consolidare tutti questi concetti vedi il seguente esempio che avvolge psycopg2

import psycopg2


class Postgres(object):
"""docstring for Postgres"""
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = object.__new__(cls)
            # normally the db_credenials would be fetched from a config file or the enviroment
            # meaning shouldn't be hardcoded as follow
            db_config = {'dbname': 'demo', 'host': 'localhost',
                     'password': 'postgres', 'port': 5432, 'user': 'postgres'}
            try:
                print('connecting to PostgreSQL database...')
                connection = Postgres._instance.connection = psycopg2.connect(**db_config)
                cursor = Postgres._instance.cursor = connection.cursor()
                cursor.execute('SELECT VERSION()')
                db_version = cursor.fetchone()

            except Exception as error:
                print('Error: connection not established {}'.format(error))
                Postgres._instance = None

            else:
                print('connection established\n{}'.format(db_version[0]))

        return cls._instance

    def __init__(self):
        self.connection = self._instance.connection
        self.cursor = self._instance.cursor

    def query(self, query):
        try:
            result = self.cursor.execute(query)
        except Exception as error:
            print('error execting query "{}", error: {}'.format(query, error))
            return None
        else:
            return result

    def __del__(self):
        self.connection.close()
        self.cursor.close()
    
risposta data 26.09.2017 - 12:19
fonte
2

Sarebbe interessante offrire funzionalità di gestione del contesto per i tuoi oggetti. Ciò significa che puoi scrivere un codice come questo:

class MyClass:
    def __init__(self):
       # connect to DB
    def __enter__(self):
       return self
    def __exit__(self):
       # close the connection

Questo ti offrirà un modo pratico per chiudere automaticamente la connessione al database chiamando la classe usando l'istruzione with:

with MyClass() as my_class:
   # do what you need
# at this point, the connection is safely closed.
    
risposta data 14.12.2017 - 09:30
fonte
-1

Molto tempo per pensarci. Oggi ho trovato la strada. non lo so è il modo migliore. crei un file con il nome: conn.py e salvalo nella cartella /usr/local/lib/python3.5/site-packages/conn/. Io uso freebsd e questo è il percorso della mia cartella dei pacchetti del sito. nella mia conn.py: conn="dbname = utente onnivoro = password postgres = 12345678"

'' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''' ''' e nello script che voglio chiamare la connessione, scrivo:

import psycopg2 importa psycopg2.extras import psycopg2.extensions

da conn import conn provare:     conn = psycopg2.connect (conn.conn) ad eccezione di:     page="Impossibile accedere al database"

cur = conn.cursor ()

e blah blah ....

spero che questo utile

    
risposta data 24.07.2016 - 06:04
fonte

Leggi altre domande sui tag