Initial commit
This commit is contained in:
10
.env
Normal file
10
.env
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Seguridad
|
||||||
|
DEBUG=True
|
||||||
|
SECRET_KEY=una-clave-muy-secreta-y-larga-123456
|
||||||
|
|
||||||
|
# Base de Datos (Conectando al PostgreSQL que instalamos)
|
||||||
|
DB_NAME=gitea
|
||||||
|
DB_USER=gitea
|
||||||
|
DB_PASSWORD=gitea_password
|
||||||
|
DB_HOST=db
|
||||||
|
DB_PORT=5432
|
||||||
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
||||||
29
Dockerfile
Normal file
29
Dockerfile
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Usamos una imagen ligera de Python
|
||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
# Evitar que Python genere archivos .pyc y que el buffer se sature
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE 1
|
||||||
|
ENV PYTHONUNBUFFERED 1
|
||||||
|
|
||||||
|
# Directorio de trabajo
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Instalar dependencias del sistema necesarias
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
libpq-dev \
|
||||||
|
gcc \
|
||||||
|
gettext \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Instalar dependencias de Python
|
||||||
|
COPY requirements.txt /app/
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Copiar el resto del código
|
||||||
|
COPY . /app/
|
||||||
|
|
||||||
|
# Exponer el puerto de Django
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Comando por defecto para arrancar (usaremos manage.py en dev y gunicorn en prod)
|
||||||
|
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
|
||||||
0
apps/__init__.py
Normal file
0
apps/__init__.py
Normal file
11
apps/common/utils.py
Normal file
11
apps/common/utils.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Ejemplo de limpieza de tipos para SQL
|
||||||
|
def clean_sql_string(value):
|
||||||
|
if value is None:
|
||||||
|
return ""
|
||||||
|
return str(value).strip().replace("'", "''")
|
||||||
|
|
||||||
|
def clean_sql_int(value):
|
||||||
|
try:
|
||||||
|
return int(value)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return 0
|
||||||
56
apps/promociones/actions.py
Normal file
56
apps/promociones/actions.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
from django.db import connection
|
||||||
|
from common.utils import clean_sql_string, clean_sql_int
|
||||||
|
|
||||||
|
def getData(params):
|
||||||
|
"""
|
||||||
|
Función estándar para obtener datos de promociones.
|
||||||
|
"""
|
||||||
|
# 1. Definimos la query con placeholders (%s)
|
||||||
|
query = """
|
||||||
|
SELECT id, nombre, fecha_inicio, descripcion
|
||||||
|
FROM promociones
|
||||||
|
WHERE id = %s AND activo = %s
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 2. Preparamos el diccionario de parámetros (tu estándar get_parameterized)
|
||||||
|
# Limpiamos los datos antes de enviarlos a la base de datos
|
||||||
|
id_promocion = clean_sql_int(params.get('id'))
|
||||||
|
is_active = 1 if params.get('activo') else 0
|
||||||
|
|
||||||
|
parameter_dict = [id_promocion, is_active]
|
||||||
|
|
||||||
|
# 3. Ejecución parametrizada
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.execute(query, parameter_dict)
|
||||||
|
columns = [col[0] for col in cursor.description]
|
||||||
|
result = [dict(zip(columns, row)) for row in cursor.fetchall()]
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def setData(params):
|
||||||
|
"""
|
||||||
|
Función estándar para insertar o actualizar (set_parameterized).
|
||||||
|
"""
|
||||||
|
# Ejemplo de UPDATE con JOIN (como solicitaste en tu estándar)
|
||||||
|
query = """
|
||||||
|
UPDATE promociones p
|
||||||
|
SET p.nombre = %s, p.fecha_modificacion = %s
|
||||||
|
FROM categorias c
|
||||||
|
WHERE p.categoria_id = c.id AND p.id = %s
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Fecha en formato Año-Día-Mes (Y-d-m)
|
||||||
|
import datetime
|
||||||
|
fecha_hoy = datetime.datetime.now().strftime('%Y-%d-%m')
|
||||||
|
|
||||||
|
parameter_dict = [
|
||||||
|
clean_sql_string(params.get('nombre')),
|
||||||
|
fecha_hoy,
|
||||||
|
clean_sql_int(params.get('id'))
|
||||||
|
]
|
||||||
|
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.execute(query, parameter_dict)
|
||||||
|
affected_rows = cursor.rowcount
|
||||||
|
|
||||||
|
return affected_rows
|
||||||
7
apps/promociones/urls.py
Normal file
7
apps/promociones/urls.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from django.urls import path
|
||||||
|
from .views import get_promocion_view
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
# Capa 1: Definición del endpoint
|
||||||
|
path('obtener/', get_promocion_view, name='get_promocion'),
|
||||||
|
]
|
||||||
54
apps/promociones/views.py
Normal file
54
apps/promociones/views.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import logging
|
||||||
|
from django.http import JsonResponse
|
||||||
|
from .actions import getData
|
||||||
|
|
||||||
|
# Configuración del logger para rastrear la ejecución
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def get_promocion_view(request):
|
||||||
|
"""
|
||||||
|
Vista estandarizada para la obtención de una promoción.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# BLOQUE 1: Log de iniciación
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
logger.info("[START] Iniciando ejecución de get_promocion_view")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# BLOQUE 2: Limpieza de datos (Data Cleaning)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Extraemos los parámetros del request y preparamos el diccionario
|
||||||
|
raw_data = request.GET.dict()
|
||||||
|
|
||||||
|
# Aquí es donde ella aplicaría validaciones adicionales si fuera necesario
|
||||||
|
clean_params = {
|
||||||
|
'id': raw_data.get('id'),
|
||||||
|
'activo': raw_data.get('activo', True) # Valor por defecto
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# BLOQUE 3: Llamada a la Action (Execution)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# La lógica de SQL y parametrización vive dentro de esta llamada
|
||||||
|
resultado_db = getData(clean_params)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# BLOQUE 4: Log de cierre y respuesta (Closure)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
logger.info(f"[SUCCESS] get_promocion_view finalizada. Registros encontrados: {len(resultado_db)}")
|
||||||
|
|
||||||
|
return JsonResponse({
|
||||||
|
'status': 'success',
|
||||||
|
'data': resultado_db
|
||||||
|
}, status=200)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Log de error detallado en caso de fallo
|
||||||
|
logger.error(f"[ERROR] Fallo crítico en get_promocion_view: {str(e)}")
|
||||||
|
|
||||||
|
return JsonResponse({
|
||||||
|
'status': 'error',
|
||||||
|
'message': 'Error interno del servidor'
|
||||||
|
}, status=500)
|
||||||
0
core/__init__.py
Normal file
0
core/__init__.py
Normal file
20
core/settings.py
Normal file
20
core/settings.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
from dotenv import load_dotenv
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Cargar variables desde el archivo .env
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
SECRET_KEY = os.getenv('SECRET_KEY')
|
||||||
|
DEBUG = os.getenv('DEBUG') == 'True'
|
||||||
|
|
||||||
|
# Configuración de base de datos usando las variables
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
|
'NAME': os.getenv('DB_NAME'),
|
||||||
|
'USER': os.getenv('DB_USER'),
|
||||||
|
'PASSWORD': os.getenv('DB_PASSWORD'),
|
||||||
|
'HOST': os.getenv('DB_HOST'),
|
||||||
|
'PORT': os.getenv('DB_PORT'),
|
||||||
|
}
|
||||||
|
}
|
||||||
6
core/urls.py
Normal file
6
core/urls.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.urls import path, include
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
# Redirigimos todas las peticiones de /api/promociones/ a nuestra app
|
||||||
|
path('api/promociones/', include('promociones.urls')),
|
||||||
|
]
|
||||||
22
docker-compose.yml
Normal file
22
docker-compose.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
networks:
|
||||||
|
# Usamos la red que creamos al principio para que el Proxy lo vea
|
||||||
|
frontend:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
container_name: django_app_dev
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- DEBUG=1
|
||||||
|
- DATABASE_URL=postgres://gitea:gitea_password@db:5432/gitea
|
||||||
|
# Usamos la base de datos de Gitea para empezar, o luego creamos una aparte
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
networks:
|
||||||
|
- frontend
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
21
manage.py
Normal file
21
manage.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""Django's command-line utility for administrative tasks."""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Run administrative tasks."""
|
||||||
|
# Apuntamos a la configuración dentro de la carpeta 'core'
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')
|
||||||
|
try:
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
except ImportError as exc:
|
||||||
|
raise ImportError(
|
||||||
|
"Couldn't import Django. Are you sure it's installed and "
|
||||||
|
"available on your PYTHONPATH environment variable? Did you "
|
||||||
|
"forget to activate a virtual environment?"
|
||||||
|
) from exc
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Django==5.0.3
|
||||||
|
psycopg2-binary==2.9.9
|
||||||
|
gunicorn==21.2.0
|
||||||
|
python-dotenv==1.0.1
|
||||||
Reference in New Issue
Block a user