The Repository pattern is a data-access design pattern in which all SQL queries are centralised inside dedicated “repository” classes, completely separate from the HTTP routes that consume them. In Hábito.,Documentation Index
Fetch the complete documentation index at: https://mintlify.com/BrandonCVale/SISTEMA-HABITOS/llms.txt
Use this file to discover all available pages before exploring further.
HabitoRepository and UsuarioRepository are the only places in the codebase that call db.session directly. Routes call repository methods; they never construct queries themselves. This separation keeps the route code thin and readable, makes the query logic easy to locate and change in one place, and makes it straightforward to swap or mock the data layer in tests without touching any controller code.
Both repository classes live under app/repositories/ and contain only @staticmethod methods. No instantiation is needed — you call them as HabitoRepository.crear_habito(...) directly.
HabitoRepository
Source: app/repositories/habito_repository.py
HabitoRepository owns all queries against the habitos and registro_habitos tables. It is imported by app/routes/habito.py and app/routes/pagina_principal.py.
obtener_habitos_por_usuario
Habito row belonging to usuario_id. The estado parameter adds an optional activo filter:
estado | SQL filter added |
|---|---|
'activos' | WHERE activo = TRUE |
'inactivos' | WHERE activo = FALSE |
'todos' (default) | No filter |
obtener_habito_por_id
Habito by primary key using db.session.get(). Returns None if no row with that ID exists. This is the method called by all routes that need to verify ownership before proceeding.
crear_habito
Habito object, adds it to the session, commits, and returns the persisted object. The descripcion parameter is optional and defaults to None (stored as NULL).
editar_habito
habito_id. If it does not exist, returns None. Otherwise updates the four mutable fields in-place and commits. Returning the updated object allows the caller to inspect the new state if needed.
eliminar_habito
Habito row with the given ID. Because the model declares cascade="all, delete-orphan" on the registros relationship, all associated RegistroHabito rows are also deleted by SQLAlchemy in the same transaction. The method is a no-op if the habit does not exist.
marcar_completado_hoy
registro_habitos for a row matching (habito_id, date.today()):
- If a record exists → delete it (unmark), subtract 1 from
racha_actual(floor 0), commit, returnFalse. - If no record exists → create one with
completado=True, incrementracha_actual, updatemejor_rachaif the new streak exceeds the previous record, commit, returnTrue.
/habitos/completar/<id> route to choose the appropriate flash message.
obtener_dias_activos_mes
set of integer day-numbers (e.g. {1, 4, 5, 12}) representing every calendar day in the given month on which the user completed at least one habit. The result is used by the dashboard to paint the heat-map calendar.
The query performs a JOIN between RegistroHabito and Habito to filter by usuario_id, and bounds the date range to the first and last day of the requested month:
UsuarioRepository
Source: app/repositories/usuario_repository.py
UsuarioRepository owns all queries against the usuarios table, including password hashing and credential verification. It is imported exclusively by app/routes/auth.py.
crear_usuario
Usuario object with the decoded hash string, adds it to the session, and commits. Raises sqlalchemy.exc.IntegrityError if either correo or nombre_usuario already exists (both columns have unique=True). The caller (auth.registro route) catches this exception and shows an error flash.
obtener_usuario_por_correo
usuarios table for a row matching the given correo value. Returns the Usuario object if found, or None if not. Uses scalar_one_or_none() — it will not raise an exception if the row is missing.
verificar_credenciales
- Calls
obtener_usuario_por_correo(correo)— returnsNoneimmediately if the email is not found. - Re-encodes both the submitted plain-text password and the stored hash as
bytes, then callsbcrypt.checkpw(). Returns theUsuarioobject on success, orNoneon a hash mismatch.
SQLAlchemy 2.0 Query Style
All queries in both repositories use the modern SQLAlchemy 2.0 Core-style select API rather than the legacyModel.query interface (which is deprecated and removed in SQLAlchemy 2.x).
Modern style (used in Hábito.)
Legacy style (not used)
| Pattern | Description |
|---|---|
db.select(Model).where(...) | Build a SELECT statement |
db.session.execute(consulta).scalars().all() | Execute and return all matching objects |
db.session.execute(consulta).scalar_one_or_none() | Execute and return one object or None |
db.session.get(Model, pk) | Fetch by primary key (identity map-aware) |
db.session.add(obj) + db.session.commit() | INSERT or track changes and persist |
db.session.delete(obj) + db.session.commit() | DELETE the row |