Search code examples
pythonoopdecoratorwrapperpython-decorators

Python - create wrapper function for SQL queries


In my project I have lots of functions that carry out SQL queries. These queries include SELECT, UPDATE, INSERT etc... When writing functions for SELECT queries for example, I write the same structure of code in every single function. e.g.

def generic_select_function(self):
    result = self.cursor.execute("""
        SQL CODE
    """)
    return result.fetchall()

To avoid repeating this code over and over I though I could create a wrapper function that inserts the SQL code into a template for SELECT queries.

I understand that a wrapper function may not even be necessary for this but I would like to try and implement one to develop my understanding of them.

What I have tried:

class DatabaseManagment():

    def __init__(self) -> None:
        self.con = sqlite3.connect("...")
        self.cursor = self.con.cursor()
        self.lock = threading.Lock()


    def sql_select(self):
        def inner(func):
            result = self.cursor.execute(f"""
                {func}
            """)
            return result.fetchall()
        return inner


    @sql_select
    def test(self):
        return "SELECT * FROM App_price"

'function' object has no attribute 'cursor'


Solution

  • Here an example of wrapper using inheritance:

    class MyQueries:
    
        @staticmethod
        def App_price():
            return "SELECT * FROM App_price"
    
    
    class MyQueriesExe(MyQueries):
    
        def select(self, func):
            return f"{self.con} executing: {func()}"
    
        def __init__(self):
            self.con = 'con id: xxx:'
    
        def App_price(self):
            return self.select(super().App_price)
    
    
    print(MyQueriesExe().App_price())
    #con id: xxx: executing: SELECT * FROM App_price
    

    Here a very basic example with a metaclass-decoration to illustrate the basic syntax

    import sqlite3 
    
    
    class SqlQueriesExe(type):
        def __new__(mcs, name:str, bases:tuple, class_dict:dict) -> type:
            cls = super().__new__(mcs, name, bases, class_dict)
    
            # decoration phase
            for method_name in ('create_table', 'insert_into_table', 'show_table'):
                app_price = getattr(cls, method_name)
                setattr(cls, app_price.__name__, mcs.executer(app_price))
    
            return cls
    
        @staticmethod
        def executer(func):
            return lambda self, *args: self.con.execute(func(self, *args))
            return lambda self, *args: self.con.executescript(func(self, *args))
    
    
    class Db(metaclass=SqlQueriesExe):
        
        def __str__(self):
            # sqlite3.sqlite_version
            # some db-info for ex pragma-stuffs
            return str(self)
    
        def __init__(self, database=":memory:"):
            self.database = database
    
        def __enter__(self):
            self.con = sqlite3.connect(self.database)
            self.cursor = self.con.cursor()
            return self
    
        def __exit__(self, exc_type, exc_value, exc_tb):
            print('db-self.database is closing.')
            # display table when closing
            print(*self.show_table())
            #self.con.rollback() # self.con.commit()
            self.con.close()
    
        def create_table(self):
            return """CREATE TABLE IF NOT EXISTS test_table (A TEXT);"""
    
        def insert_into_table(self):
            return """INSERT INTO test_table (A) VALUES (2);"""
    
        def show_table(self):
            return """SELECT * FROM test_table;"""
    
    
    with Db(":memory:") as db:
        db.create_table()
        db.insert_into_table()