diff --git a/projects/DatabaseConnectionWithOOP/classes.py b/projects/DatabaseConnectionWithOOP/classes.py new file mode 100644 index 00000000..914c9e6f --- /dev/null +++ b/projects/DatabaseConnectionWithOOP/classes.py @@ -0,0 +1,150 @@ +from abc import ABC, abstractmethod +import sqlite3 + +# Abstract Classes +class AbstractDatabase(ABC): + @abstractmethod + def close(self) -> None: + pass + +# Interfaces +class I_CommonSelectQueries(ABC): + @abstractmethod + def execute_simple_select_query(self, table_name:str, fields:list[str]) -> list: + pass + +class I_CommonTableQueries(ABC): + @abstractmethod + def create_table(self, table_name:str, fields:list[str]) -> None: + pass + + +# Classes +class Database(AbstractDatabase): + def __init__(self, database_name:str) -> None: + """ + Creates an object of Database type, connecting to a simple SQLite3 database. + + Attributes: + - database_name (str): Name of the database. + """ + self.__database_name = database_name + self.__conn = sqlite3.connect(f'{self.__database_name}.db') + + @property + def database_name(self) -> str: + return self.__database_name + + @property + def conn(self) -> object | None: + return self.__conn + + def close(self): + """ + Closes database connection. + """ + self.conn.close() + +class Cursor(): + def __init__(self, database_object:Database) -> None: + """ + Creates a cursor object in base of the received database_object as parameter. + + Attributes: + - database_object (Database): Database connection object to create the cursor. + """ + self.database = database_object + self.__cursor = self.database.conn.cursor() + + @property + def cursor(self) -> object: + return self.__cursor + + def execute_query(self, query:str) -> list | None: + """ + The Cursor object executes an SQL query that the user will pass as str, for example 'SELECT * FROM Users WHERE age > 21'. + + Attributes: + - query (str): The query that will be executed, it can be of any type (SELECT, UPDATE, DELETE, INSERT, etc.). + """ + try: + result = self.cursor.execute(query) + print('Query was executed.') + return result.fetchall() if result else [] + except Exception as e: + print('Impossible to execute the query:', e) + return None + +# Custom cursor for most common queries execution. +class CustomCursor(Cursor, I_CommonTableQueries, I_CommonSelectQueries): + def __init__(self, database_object:Database): + super().__init__(database_object) + + def execute_query(self, query:str): + """ + Executes a simple query of any type wether SELECT, INSERT, CREATE TABLE, DELETE, etc. + This method leverages its class's parent class method with the same name, behavior and attributes. + + Attributes: + - query (str): The query that will be executed. + """ + return super().execute_query(query) + + def execute_simple_select_query(self, table_name:str, fields:list[str]) -> list: + """ + Executes a simple query of SELECT type with the fields to show provided as parameters and the name of the table. + + Attributes: + - table_name (str): Name of the table from where data is going to be obtained. + - fields (list[str]): List of fields to fetch from the database's table. + + Return: + - data (list): List of rows obtained from the SELECT query in str format. + """ + query = f"""SELECT {', '.join(fields)} FROM {table_name};""" + data = self.execute_query(query) + return data + + def create_table(self, table_name:str, fields:list[str]) -> None: + """ + Creates a table on the database based on the provided fields and table name. + + Attributes: + - table_name (): Name of the table that is going to be created. + -fields (list): List of the table fields in string format. + + Return: + - None + """ + + # Format the fields from fields list in a single string + formated_fields = ', '.join(fields) + + query = f"""CREATE TABLE IF NOT EXISTS {table_name} ({formated_fields})""" + self.execute_query(query) + + def insert_rows(self, table_name:str, fields:list[str], values:list[list[str]]) -> bool: + """ + Executes a classic INSERT query receiving the name of the fields matching the values to insert and the name of the table + This method iterates over every row on the provided list of values to make an insertion on the database in the table.. + + Attributes: + - table_name (str): Name of the table where the data will be inserted as a new row. + - fields (list[str]): List of fields to determine which type of data should be inserted. + - values (list[list[str]]): List of rows to insert, every row is a list of every value in the desired format. + + Return: + - succes (str): True if rows were inserted. False if it was not possible to insert rows. + """ + formated_fields = ', '.join(fields) + rows_list = values + for row in rows_list: + try: + formated_values = ', '.join(f"'{value}'" for value in row) + query = f"""INSERT INTO {table_name} ({formated_fields}) VALUES({formated_values});""" + self.execute_query(query) + except Exception as e: + print(f'Error during inserting data into database on table {table_name}:', e) + succes = False + succes = True + return succes \ No newline at end of file diff --git a/projects/DatabaseConnectionWithOOP/main.py b/projects/DatabaseConnectionWithOOP/main.py new file mode 100644 index 00000000..1fa6a049 --- /dev/null +++ b/projects/DatabaseConnectionWithOOP/main.py @@ -0,0 +1,27 @@ +from classes import Database, CustomCursor + + +""" Now with a simple cursor object we can execute the most common queries like the typical 'SELECT', or create a table with ease reducing the SQL statement we have to provide to just parameters. """ +my_database = Database('MyDatabase') +my_cursor = CustomCursor(my_database) + +# Creating users table +my_cursor.create_table('users', ['id INTEGER PRIMARY KEY AUTOINCREMENT', 'username TEXT UNIQUE NOT NULL', 'email TEXT UNIQUE NOT NULL', 'password TEXT NOT NULL', 'age INTEGER']) + +# Inserting data in users table +users_to_insert = [ + ['Joaquin', 'joaquin@gmail.com', '123password', 22], + ['Aline', 'aline@gmail.com', '123password', 21], + ['Henry', 'henry@gmail.com', '123password', 35], + ['James', 'james@gmail.com', '123password', 32], + ['Nicole', 'nicole@gmail.com', '123password', 28], + ['Maria', 'maria@gmail.com', '123password', 38] +] +users_fields = ['username', 'email', 'password', 'age'] +my_cursor.insert_rows('users', users_fields, users_to_insert) + +# Fetching data from database in users table +result = my_cursor.execute_simple_select_query('users', users_fields) +result2 = my_cursor.execute_simple_select_query('users', ['username', 'age']) +print('First fetch:', result) +print('Second fetch:', result2) \ No newline at end of file diff --git a/projects/DatabaseConnectionWithOOP/requirements.txt b/projects/DatabaseConnectionWithOOP/requirements.txt new file mode 100644 index 00000000..cbc50d00 --- /dev/null +++ b/projects/DatabaseConnectionWithOOP/requirements.txt @@ -0,0 +1,70 @@ +aiohappyeyeballs==2.4.6 +aiohttp==3.11.12 +aiosignal==1.3.2 +annotated-types==0.7.0 +anyio==4.8.0 +asgiref==3.8.1 +attrs==25.1.0 +blinker==1.9.0 +certifi==2024.8.30 +chardet==5.2.0 +charset-normalizer==3.4.0 +click==8.1.8 +colorama==0.4.6 +Deprecated==1.2.15 +dj-database-url==2.3.0 +Django==5.1.6 +django-environ==0.12.0 +django-oauth2-provider==0.2.6.1 +django-redis==5.4.0 +djangorestframework==3.15.2 +djangorestframework-oauth==1.1.0 +djangorestframework_simplejwt==5.4.0 +drf-yasg==1.21.9 +fastapi==0.115.6 +Flask==3.1.0 +frozenlist==1.5.0 +h11==0.14.0 +idna==3.10 +inflection==0.5.1 +itsdangerous==2.2.0 +Jinja2==3.1.5 +limits==4.0.0 +MarkupSafe==3.0.2 +multidict==6.1.0 +packaging==24.2 +pillow==11.1.0 +prettytable==3.14.0 +propcache==0.2.1 +psutil==7.0.0 +psycopg2==2.9.10 +psycopg2-binary==2.9.10 +pydantic==2.10.5 +pydantic_core==2.27.2 +PyDirectInput==1.0.4 +PyJWT==2.10.1 +pynput==1.7.7 +python-dotenv==1.0.1 +python-magic==0.4.27 +python-magic-bin==0.4.14 +pytz==2025.1 +PyYAML==6.0.2 +redis==5.2.1 +reportlab==4.3.1 +requests==2.32.3 +shortuuid==1.0.13 +six==1.17.0 +slowapi==0.1.9 +sniffio==1.3.1 +sqlparse==0.5.3 +starlette==0.41.3 +translation==1.0.5 +typing_extensions==4.12.2 +tzdata==2025.1 +uritemplate==4.1.1 +urllib3==2.2.3 +uvicorn==0.34.0 +wcwidth==0.2.13 +Werkzeug==3.1.3 +wrapt==1.17.2 +yarl==1.18.3