Skip to content
This repository was archived by the owner on Apr 24, 2025. It is now read-only.

New project: DatabaseConnection module using OOP principles. #818

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions projects/DatabaseConnectionWithOOP/classes.py
Original file line number Diff line number Diff line change
@@ -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
27 changes: 27 additions & 0 deletions projects/DatabaseConnectionWithOOP/main.py
Original file line number Diff line number Diff line change
@@ -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', '[email protected]', '123password', 22],
['Aline', '[email protected]', '123password', 21],
['Henry', '[email protected]', '123password', 35],
['James', '[email protected]', '123password', 32],
['Nicole', '[email protected]', '123password', 28],
['Maria', '[email protected]', '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)
70 changes: 70 additions & 0 deletions projects/DatabaseConnectionWithOOP/requirements.txt
Original file line number Diff line number Diff line change
@@ -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