The Data Access Object (DAO) pattern is a structural pattern that cleanly separates your domain objects from the code that reads and writes them to a database. Instead of scattering SQL strings across your application, every database interaction lives in one dedicated class — the DAO — making your code easier to test, maintain, and extend. In this course’s implementation, the pattern is built from three layers:Documentation Index
Fetch the complete documentation index at: https://mintlify.com/HotCode2025/Print-Estoy-Cansado-Jefe-TercerSemestre/llms.txt
Use this file to discover all available pages before exploring further.
| Layer | Class | Responsibility |
|---|---|---|
| Domain Object | Persona | Holds data; no SQL knowledge |
| Connection Manager | Conexion | Singleton connection + cursor |
| Data Access Object | PersonaDao | All CRUD SQL operations |
The Persona Class
Persona is a plain Python domain object. It stores the four columns that map to the persona table and exposes them exclusively through explicit getters and setters — private attributes (name-mangled with __) enforce encapsulation.
persona.py
All attributes use Python’s name-mangling convention (
__attribute), making them private to the class. External code must always go through the getter/setter interface — direct attribute access like persona.__nombre will raise an AttributeError.__str__ method makes every Persona instance directly printable, which is useful when logging or debugging a list of records.
The Conexion Class
Conexion implements a singleton connection and cursor. All class-level attributes (database credentials and the live __conexion/__cursor references) are shared across all callers — there is only ever one open connection for the entire application’s lifetime.
Credentials are pulled from environment variables at class definition time via python-dotenv, so no credentials ever appear in source code.
Define class-level credentials
All connection parameters are declared as class attributes and read from the environment using
os.getenv(). Default values are provided as a fallback.Lazy-initialize the connection
obtenerConexion() checks whether __conexion is None before calling psycopg2.connect(). Subsequent calls return the already-open connection immediately.Lazy-initialize the cursor
obtenerCursor() follows the same pattern — it calls obtenerConexion() internally and creates the cursor only once.conexion.py
Conexion wraps each psycopg2 call in try/except and logs success or failure with log.debug() / log.error().
The PersonaDao Class
PersonaDao owns all SQL strings as private class constants and exposes four @classmethod operations — one for each CRUD verb. No instance of PersonaDao is ever created; callers invoke the methods directly on the class.
Using
@classmethod (rather than instance methods or static methods) gives each method access to the class itself via the cls parameter. This means the SQL constants (cls.__SELECCIONAR, etc.) are accessible without creating an object, and a subclass could override them if you ever need a specialized DAO.persona_dao.py
Method reference
seleccionar() → list[Persona]
seleccionar() → list[Persona]
Executes
SELECT id_persona, nombre, apellido, email FROM persona with no parameters. Fetches all rows and maps each tuple to a Persona object using a list comprehension. Returns an empty list when the table is empty.insertar(persona: Persona) → int
insertar(persona: Persona) → int
Executes
INSERT INTO persona (nombre, apellido, email) VALUES (%s, %s, %s) using parameterized values retrieved through the Persona getters. Commits the transaction and returns cursor.rowcount (typically 1 on success).actualizando(persona: Persona) → int
actualizando(persona: Persona) → int
Executes
UPDATE persona SET nombre=%s, apellido=%s, email=%s WHERE id_persona=%s. The tuple order is (nombre, apellido, email, id_persona) — the primary key comes last because it appears in the WHERE clause. Returns the number of rows updated.eliminar(persona: Persona) → int
eliminar(persona: Persona) → int
Executes
DELETE FROM persona WHERE id_persona=%s using only the id_persona field. The Persona object passed in can be created with just the ID set: Persona(id_persona=1). Returns the number of rows deleted.Wiring It Together — main.py
The entry point imports all three classes and demonstrates a full CRUD cycle in order: insert → select → update → delete → close.
main.py
main.py never touches SQL directly — it only works with Persona objects and calls methods on PersonaDao. That is the entire point of the DAO pattern: business logic is completely isolated from persistence logic.
Using .env for Configuration
Hard-coding database credentials in source code is a security risk. The project uses python-dotenv to load them from a .env file at startup.
Building the DAO from Scratch
Install dependencies
Install the required packages for database connectivity and environment variable loading.
Create the .env file
Add your PostgreSQL credentials to a
.env file in your project root. Never commit this file..env
Define the Persona domain class
Create
persona.py with private attributes and explicit getters/setters. This class has zero knowledge of SQL or databases.persona.py
Build the Conexion singleton
Create
conexion.py. The @classmethod decorator ensures the same connection instance is reused across all callers.conexion.py
Implement the PersonaDao CRUD methods
Create
persona_dao.py. Declare all SQL as private class constants and implement each CRUD operation as a @classmethod.persona_dao.py