[TAREA GUIADA] Crear app productos con endpoints obtener y guardar #2

Open
opened 2026-04-17 21:24:54 +00:00 by jjminguez · 0 comments
Owner

Descripcion

Bienvenida al equipo! Esta es tu primera tarea. Esta pensada para que aprendas la arquitectura del proyecto paso a paso.

El objetivo es crear una nueva app Django llamada productos siguiendo exactamente el mismo patron que ya existe en promociones. Tendras que crear el modelo, dos endpoints (obtener y guardar), los logs y los datos de prueba.


0. Preparar el entorno local

0.1 Clonar el repositorio

Abre CMD o PowerShell y ejecuta:

git clone https://git.v-encore-lab.com/Proyecto-SaaS/django-core-base.git
cd django-core-base

0.2 Obtener el fichero .env

Los .env nunca se versionan en el repo del proyecto. Estan en un repo separado llamado environments:

https://git.v-encore-lab.com/Proyecto-SaaS/environments

Navega a django-core-base/pre_dev/ y descarga el fichero .env.

Copiatelo dentro del repo clonado en esta ruta exacta:

django-core-base/deployments/.env

0.3 Levantar Docker

cd deployments
docker-compose up --build -d

La primera vez tarda unos minutos descargando imagenes. Verifica que arranca:

docker-compose logs -f

Deberias ver:

--> Ejecutando migraciones...
--> Arrancando servidor con Gunicorn...

La API estara disponible en: http://localhost:8000


POSIBLE ERROR: entrypoint con saltos de linea incorrectos (CRLF)

Si ves este error al levantar Docker:

/bin/sh: ./entrypoint.sh: not found

o

exec format error

Ejecuta esto desde la carpeta del repo y vuelve a levantar:

git config core.autocrlf false
git rm --cached deployments/entrypoint.sh
git checkout deployments/entrypoint.sh
docker-compose up --build -d

1. Crear los ficheros de la app

Crea la carpeta app/productos/ y dentro los siguientes ficheros:


1.1 app/productos/__init__.py

Fichero vacio. Solo crealo.


1.2 app/productos/apps.py

from django.apps import AppConfig

class ProductosConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'productos'

1.3 app/productos/models.py

from django.db import models

class Producto(models.Model):
    nombre = models.CharField(max_length=255)
    descripcion = models.TextField(null=True, blank=True)
    precio = models.FloatField(default=0.0)
    activo = models.BooleanField(default=True)
    fecha_creacion = models.DateField(null=True, blank=True)

    class Meta:
        db_table = 'productos'

    def __str__(self):
        return self.nombre

Por que db_table? Sin esto Django llamaria a la tabla productos_producto. Con esto la llama productos, que es el nombre correcto para las queries SQL.


1.4 app/productos/acciones.py

Aqui va toda la logica de negocio. Las queries usan placeholders %snunca concatenes strings con datos del usuario (SQL injection):

from django.db import connection
from common.utils import clean_sql_string, clean_sql_int


def getData(params):
    query = (
        "SELECT id, nombre, descripcion, precio, activo, fecha_creacion "
        "FROM productos "
        "WHERE id = %s AND activo = %s"
    )
    id_producto = clean_sql_int(params.get('id'))
    is_active = True if params.get('activo') else False

    with connection.cursor() as cursor:
        cursor.execute(query, [id_producto, is_active])
        columns = [col[0] for col in cursor.description]
        result = [dict(zip(columns, row)) for row in cursor.fetchall()]

    return result


def setData(params):
    query = (
        "INSERT INTO productos (nombre, descripcion, precio, activo, fecha_creacion) "
        "VALUES (%s, %s, %s, %s, CURRENT_DATE) "
        "RETURNING id"
    )
    with connection.cursor() as cursor:
        cursor.execute(query, [
            clean_sql_string(params.get('nombre')),
            clean_sql_string(params.get('descripcion', '')),
            params.get('precio', 0.0),
            True,
        ])
        new_id = cursor.fetchone()[0]

    return {'id': new_id, 'mensaje': 'Producto creado correctamente'}

Por que RETURNING id? En PostgreSQL es la unica forma correcta de recuperar el id del INSERT. Nunca uses cursor.lastrowid con psycopg2, no funciona.


1.5 app/productos/views.py

Cada endpoint tiene 4 bloques obligatorios con LogService. Esto es el estandar del proyecto para auditoria de todas las llamadas:

from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.authentication import JWTAuthentication
from django.http import JsonResponse

from general.utilidades.acciones import LogService
from .acciones import getData, setData


class ProductoObtener(APIView):
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    def post(self, request):
        path = '/productos/obtener/'

        # Bloque 1 - Inicio log
        log_id = LogService.gestionar_log(self, request, path=path)

        try:
            # Bloque 2 - Limpieza de datos
            data = request.data
            LogService.gestionar_log(self, request, log_id=log_id,
                                     path=path, body_request=data, status_code=100)
            params = {'id': data.get('id'), 'activo': data.get('activo', True)}

        except Exception as error:
            response = {'error': str(error)}
            LogService.gestionar_log(self, request, log_id=log_id,
                                     path=path, body_response=response, status_code=400)
            return JsonResponse(response, status=400)

        try:
            # Bloque 3 - Accion
            resultado = getData(params)
            LogService.gestionar_log(self, request, log_id=log_id,
                                     path=path, body_response=resultado, status_code=200)
            return JsonResponse(resultado, safe=False, status=200)

        except Exception as error:
            response = {'error': str(error)}
            LogService.gestionar_log(self, request, log_id=log_id,
                                     path=path, body_response=response, status_code=500)
            return JsonResponse(response, status=500)


class ProductoGuardar(APIView):
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    def post(self, request):
        path = '/productos/guardar/'

        # Bloque 1 - Inicio log
        log_id = LogService.gestionar_log(self, request, path=path)

        try:
            # Bloque 2 - Limpieza de datos
            data = request.data
            LogService.gestionar_log(self, request, log_id=log_id,
                                     path=path, body_request=data, status_code=100)
            params = {
                'nombre': data.get('nombre'),
                'descripcion': data.get('descripcion', ''),
                'precio': data.get('precio', 0.0),
            }

        except Exception as error:
            response = {'error': str(error)}
            LogService.gestionar_log(self, request, log_id=log_id,
                                     path=path, body_response=response, status_code=400)
            return JsonResponse(response, status=400)

        try:
            # Bloque 3 - Accion
            resultado = setData(params)
            LogService.gestionar_log(self, request, log_id=log_id,
                                     path=path, body_response=resultado, status_code=200)
            return JsonResponse(resultado, safe=False, status=200)

        except Exception as error:
            response = {'error': str(error)}
            LogService.gestionar_log(self, request, log_id=log_id,
                                     path=path, body_response=response, status_code=500)
            return JsonResponse(response, status=500)

1.6 app/productos/urls.py

from django.urls import path
from .views import ProductoObtener, ProductoGuardar

urlpatterns = [
    path('obtener/', ProductoObtener.as_view(), name='producto_obtener'),
    path('guardar/', ProductoGuardar.as_view(), name='producto_guardar'),
]

1.7 app/productos/fixtures/semillas.json

Crea la carpeta fixtures/ dentro de productos/ y el fichero con estos datos de prueba:

[
  {
    "model": "productos.producto",
    "pk": 1,
    "fields": {
      "nombre": "Producto Ejemplo A",
      "descripcion": "Descripcion del producto A",
      "precio": 9.99,
      "activo": true,
      "fecha_creacion": "2026-01-01"
    }
  },
  {
    "model": "productos.producto",
    "pk": 2,
    "fields": {
      "nombre": "Producto Ejemplo B",
      "descripcion": "Descripcion del producto B",
      "precio": 24.50,
      "activo": true,
      "fecha_creacion": "2026-02-15"
    }
  },
  {
    "model": "productos.producto",
    "pk": 3,
    "fields": {
      "nombre": "Producto Inactivo",
      "descripcion": "Este no deberia aparecer al filtrar activo=true",
      "precio": 5.00,
      "activo": false,
      "fecha_creacion": "2026-03-01"
    }
  }
]

El entrypoint.sh ejecuta loaddata semillas al arrancar el servidor. Estas semillas se cargan automaticamente.


2. Registrar la app en Django

2.1 app/api_config/settings.py

Busca INSTALLED_APPS y anade 'productos':

INSTALLED_APPS = [
    ...
    'general',
    'promociones',
    'productos',       # <-- anadir aqui
    'automatizados',
    'backend_admin',
    'common',
]

2.2 app/api_config/urls.py

Anade la ruta de productos:

urlpatterns = [
    path('admin/', include('backend_admin.urls')),
    path('api/general/', include('general.urls')),
    path('api/promociones/', include('promociones.urls')),
    path('api/productos/', include('productos.urls')),      # <-- anadir aqui
    path('api/automatizados/', include('automatizados.urls')),
    path('api/token/', admin_views.api_token, name='token_obtain_pair'),
]

3. Crear y aplicar las migraciones

Desde la carpeta deployments/:

docker-compose exec web python manage.py makemigrations productos
docker-compose exec web python manage.py migrate

Para cargar las semillas manualmente sin reiniciar:

docker-compose exec web python manage.py loaddata semillas

4. Probar con Hoppscotch

Entra en https://hoppscotch.io (funciona en el navegador, sin instalar nada).

4.1 Importar la coleccion y los entornos

En el repo django-core-base hay dos ficheros en la raiz:

  • postman_collection.json - las llamadas
  • postman_environments.json - los entornos (LOCAL, DEV, PROD)

En Hoppscotch:

  1. Collections -> Import -> Postman -> selecciona postman_collection.json
  2. Environments -> Import -> selecciona postman_environments.json -> activa el entorno LOCAL

4.2 Obtener el token

Ejecuta primero Auth -> Obtener Token (Login). El script de la coleccion guarda el access_token automaticamente.

4.3 Probar obtener

Crea una nueva llamada manual:

  • Metodo: POST
  • URL: {{base_url}}/api/productos/obtener/
  • Header: Authorization: Bearer {{access_token}}
  • Body (JSON):
{ "id": 1, "activo": true }

Resultado esperado: datos del Producto Ejemplo A.

4.4 Probar guardar

  • Metodo: POST
  • URL: {{base_url}}/api/productos/guardar/
  • Header: Authorization: Bearer {{access_token}}
  • Body (JSON):
{
  "nombre": "Mi primer producto",
  "descripcion": "Creado desde Hoppscotch",
  "precio": 15.99
}

Resultado esperado: {"id": 4, "mensaje": "Producto creado correctamente"}


5. Subir los cambios

Importante: de momento trabajaras unicamente en la rama pre-dev. No toques dev ni master hasta que tengas mas experiencia con el flujo del proyecto.

Con GitHub Desktop (recomendado para empezar)

  1. Abre GitHub Desktop
  2. En el menu superior: File -> Add local repository -> selecciona la carpeta django-core-base
  3. En la barra de ramas (arriba a la izquierda) verifica que estas en pre-dev. Si no, haz clic en el desplegable y seleccionala. Si no aparece, haz clic en Fetch origin primero.
  4. En el panel izquierdo veras todos los ficheros que has creado o modificado marcados en verde/amarillo
  5. Abajo a la izquierda escribe el mensaje del commit:
    • Summary: feat: crear app productos con endpoints obtener y guardar
  6. Haz clic en Commit to pre-dev
  7. Haz clic en Push origin (boton azul arriba a la derecha)

Listo. Los cambios estaran en la rama pre-dev del repositorio.

Con CMD (alternativa)

Si prefieres la terminal:

git checkout pre-dev
git add .
git commit -m "feat: crear app productos con endpoints obtener y guardar"
git push origin pre-dev

6. Verificar con HeidiSQL

HeidiSQL es la herramienta para conectarse directamente a la base de datos PostgreSQL que corre dentro de Docker y ver los datos y los logs de tus llamadas.

6.1 Conectarse a la base de datos

  1. Abre HeidiSQL
  2. Haz clic en Nueva (esquina inferior izquierda)
  3. Rellena los campos:
    • Tipo de red: PostgreSQL (TCP/IP)
    • Host / IP: localhost
    • Puerto: 5432
    • Usuario: django_user
    • Contrasena: django_password
    • Base de datos: django_test
  4. Haz clic en Abrir

Si no conecta, verifica que el contenedor esta corriendo con docker-compose logs -f desde la carpeta deployments/.


6.2 Ver los logs de tus endpoints

Cada llamada que haces a la API queda registrada automaticamente en la tabla audit_logs gracias al LogService.

Abre una nueva pestana de consulta en HeidiSQL (Ctrl+T) y ejecuta:

SELECT id, "user", path, method, status_code, request, response, "createdAt"
FROM audit_logs
WHERE path LIKE '/productos/%'
ORDER BY "createdAt" DESC
LIMIT 20;

Veras una fila por cada bloque de LogService que se ejecuto. El campo status_code indica en que punto esta el log:

status_code Significado
0 Inicio de la llamada (Bloque 1)
100 Datos recibidos y limpiados (Bloque 2)
200 Llamada completada con exito
400 Error en los datos de entrada
500 Error interno del servidor

Los campos request y response son JSON — HeidiSQL los muestra como texto, puedes copiarlos y pegarlos en cualquier visor JSON online para verlos bien formateados.


6.3 Ver los productos de la tabla

Para ver todos los productos que hay en la base de datos:

SELECT * FROM productos ORDER BY id DESC;

Para comprobar que el ultimo producto que insertaste con guardar llego correctamente:

SELECT * FROM productos WHERE id = (SELECT MAX(id) FROM productos);

6.4 Ver el log completo de una llamada concreta

Si quieres ver exactamente que recibio y que respondio el endpoint guardar en cada llamada:

SELECT id, path, status_code, request, response, "createdAt"
FROM audit_logs
WHERE path = '/productos/guardar/'
ORDER BY "createdAt" DESC
LIMIT 5;

Y para obtener:

SELECT id, path, status_code, request, response, "createdAt"
FROM audit_logs
WHERE path = '/productos/obtener/'
ORDER BY "createdAt" DESC
LIMIT 5;

Truco: si una llamada devolvio error 500, busca en audit_logs la fila con status_code = '500' de esa ruta — el campo response tendra el mensaje de error exacto que lanzo Python, lo que te ayudara a depurar sin tener que mirar los logs de Docker.


Checklist de entrega

  • app/productos/__init__.py
  • app/productos/apps.py
  • app/productos/models.py con 5 campos
  • app/productos/acciones.py con getData y setData
  • app/productos/views.py con 4 bloques LogService en cada endpoint
  • app/productos/urls.py
  • app/productos/fixtures/semillas.json con 3 productos
  • productos en INSTALLED_APPS
  • api/productos/ en api_config/urls.py
  • Migraciones creadas y aplicadas
  • Endpoint obtener funcionando en Hoppscotch
  • Endpoint guardar funcionando en Hoppscotch
  • Cambios subidos a pre-dev

Cualquier duda, pregunta directamente. Animo!

## Descripcion Bienvenida al equipo! Esta es tu primera tarea. Esta pensada para que aprendas la arquitectura del proyecto paso a paso. El objetivo es crear una nueva app Django llamada **`productos`** siguiendo exactamente el mismo patron que ya existe en `promociones`. Tendras que crear el modelo, dos endpoints (`obtener` y `guardar`), los logs y los datos de prueba. --- ## 0. Preparar el entorno local ### 0.1 Clonar el repositorio Abre **CMD o PowerShell** y ejecuta: ``` git clone https://git.v-encore-lab.com/Proyecto-SaaS/django-core-base.git cd django-core-base ``` ### 0.2 Obtener el fichero `.env` Los `.env` nunca se versionan en el repo del proyecto. Estan en un repo separado llamado **`environments`**: ``` https://git.v-encore-lab.com/Proyecto-SaaS/environments ``` Navega a `django-core-base/pre_dev/` y descarga el fichero `.env`. Copiatelo dentro del repo clonado en esta ruta exacta: ``` django-core-base/deployments/.env ``` ### 0.3 Levantar Docker ``` cd deployments docker-compose up --build -d ``` La primera vez tarda unos minutos descargando imagenes. Verifica que arranca: ``` docker-compose logs -f ``` Deberias ver: ``` --> Ejecutando migraciones... --> Arrancando servidor con Gunicorn... ``` La API estara disponible en: **http://localhost:8000** --- ### POSIBLE ERROR: entrypoint con saltos de linea incorrectos (CRLF) Si ves este error al levantar Docker: ``` /bin/sh: ./entrypoint.sh: not found ``` o ``` exec format error ``` Ejecuta esto desde la carpeta del repo y vuelve a levantar: ``` git config core.autocrlf false git rm --cached deployments/entrypoint.sh git checkout deployments/entrypoint.sh docker-compose up --build -d ``` --- ## 1. Crear los ficheros de la app Crea la carpeta `app/productos/` y dentro los siguientes ficheros: --- ### 1.1 `app/productos/__init__.py` Fichero vacio. Solo crealo. --- ### 1.2 `app/productos/apps.py` ```python from django.apps import AppConfig class ProductosConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'productos' ``` --- ### 1.3 `app/productos/models.py` ```python from django.db import models class Producto(models.Model): nombre = models.CharField(max_length=255) descripcion = models.TextField(null=True, blank=True) precio = models.FloatField(default=0.0) activo = models.BooleanField(default=True) fecha_creacion = models.DateField(null=True, blank=True) class Meta: db_table = 'productos' def __str__(self): return self.nombre ``` > **Por que `db_table`?** Sin esto Django llamaria a la tabla `productos_producto`. Con esto la llama `productos`, que es el nombre correcto para las queries SQL. --- ### 1.4 `app/productos/acciones.py` Aqui va toda la logica de negocio. Las queries usan placeholders `%s` — **nunca concatenes strings** con datos del usuario (SQL injection): ```python from django.db import connection from common.utils import clean_sql_string, clean_sql_int def getData(params): query = ( "SELECT id, nombre, descripcion, precio, activo, fecha_creacion " "FROM productos " "WHERE id = %s AND activo = %s" ) id_producto = clean_sql_int(params.get('id')) is_active = True if params.get('activo') else False with connection.cursor() as cursor: cursor.execute(query, [id_producto, is_active]) columns = [col[0] for col in cursor.description] result = [dict(zip(columns, row)) for row in cursor.fetchall()] return result def setData(params): query = ( "INSERT INTO productos (nombre, descripcion, precio, activo, fecha_creacion) " "VALUES (%s, %s, %s, %s, CURRENT_DATE) " "RETURNING id" ) with connection.cursor() as cursor: cursor.execute(query, [ clean_sql_string(params.get('nombre')), clean_sql_string(params.get('descripcion', '')), params.get('precio', 0.0), True, ]) new_id = cursor.fetchone()[0] return {'id': new_id, 'mensaje': 'Producto creado correctamente'} ``` > **Por que `RETURNING id`?** En PostgreSQL es la unica forma correcta de recuperar el id del INSERT. Nunca uses `cursor.lastrowid` con psycopg2, no funciona. --- ### 1.5 `app/productos/views.py` Cada endpoint tiene **4 bloques obligatorios** con LogService. Esto es el estandar del proyecto para auditoria de todas las llamadas: ```python from rest_framework.views import APIView from rest_framework.permissions import IsAuthenticated from rest_framework_simplejwt.authentication import JWTAuthentication from django.http import JsonResponse from general.utilidades.acciones import LogService from .acciones import getData, setData class ProductoObtener(APIView): authentication_classes = [JWTAuthentication] permission_classes = [IsAuthenticated] def post(self, request): path = '/productos/obtener/' # Bloque 1 - Inicio log log_id = LogService.gestionar_log(self, request, path=path) try: # Bloque 2 - Limpieza de datos data = request.data LogService.gestionar_log(self, request, log_id=log_id, path=path, body_request=data, status_code=100) params = {'id': data.get('id'), 'activo': data.get('activo', True)} except Exception as error: response = {'error': str(error)} LogService.gestionar_log(self, request, log_id=log_id, path=path, body_response=response, status_code=400) return JsonResponse(response, status=400) try: # Bloque 3 - Accion resultado = getData(params) LogService.gestionar_log(self, request, log_id=log_id, path=path, body_response=resultado, status_code=200) return JsonResponse(resultado, safe=False, status=200) except Exception as error: response = {'error': str(error)} LogService.gestionar_log(self, request, log_id=log_id, path=path, body_response=response, status_code=500) return JsonResponse(response, status=500) class ProductoGuardar(APIView): authentication_classes = [JWTAuthentication] permission_classes = [IsAuthenticated] def post(self, request): path = '/productos/guardar/' # Bloque 1 - Inicio log log_id = LogService.gestionar_log(self, request, path=path) try: # Bloque 2 - Limpieza de datos data = request.data LogService.gestionar_log(self, request, log_id=log_id, path=path, body_request=data, status_code=100) params = { 'nombre': data.get('nombre'), 'descripcion': data.get('descripcion', ''), 'precio': data.get('precio', 0.0), } except Exception as error: response = {'error': str(error)} LogService.gestionar_log(self, request, log_id=log_id, path=path, body_response=response, status_code=400) return JsonResponse(response, status=400) try: # Bloque 3 - Accion resultado = setData(params) LogService.gestionar_log(self, request, log_id=log_id, path=path, body_response=resultado, status_code=200) return JsonResponse(resultado, safe=False, status=200) except Exception as error: response = {'error': str(error)} LogService.gestionar_log(self, request, log_id=log_id, path=path, body_response=response, status_code=500) return JsonResponse(response, status=500) ``` --- ### 1.6 `app/productos/urls.py` ```python from django.urls import path from .views import ProductoObtener, ProductoGuardar urlpatterns = [ path('obtener/', ProductoObtener.as_view(), name='producto_obtener'), path('guardar/', ProductoGuardar.as_view(), name='producto_guardar'), ] ``` --- ### 1.7 `app/productos/fixtures/semillas.json` Crea la carpeta `fixtures/` dentro de `productos/` y el fichero con estos datos de prueba: ```json [ { "model": "productos.producto", "pk": 1, "fields": { "nombre": "Producto Ejemplo A", "descripcion": "Descripcion del producto A", "precio": 9.99, "activo": true, "fecha_creacion": "2026-01-01" } }, { "model": "productos.producto", "pk": 2, "fields": { "nombre": "Producto Ejemplo B", "descripcion": "Descripcion del producto B", "precio": 24.50, "activo": true, "fecha_creacion": "2026-02-15" } }, { "model": "productos.producto", "pk": 3, "fields": { "nombre": "Producto Inactivo", "descripcion": "Este no deberia aparecer al filtrar activo=true", "precio": 5.00, "activo": false, "fecha_creacion": "2026-03-01" } } ] ``` > El `entrypoint.sh` ejecuta `loaddata semillas` al arrancar el servidor. Estas semillas se cargan automaticamente. --- ## 2. Registrar la app en Django ### 2.1 `app/api_config/settings.py` Busca `INSTALLED_APPS` y anade `'productos'`: ```python INSTALLED_APPS = [ ... 'general', 'promociones', 'productos', # <-- anadir aqui 'automatizados', 'backend_admin', 'common', ] ``` ### 2.2 `app/api_config/urls.py` Anade la ruta de productos: ```python urlpatterns = [ path('admin/', include('backend_admin.urls')), path('api/general/', include('general.urls')), path('api/promociones/', include('promociones.urls')), path('api/productos/', include('productos.urls')), # <-- anadir aqui path('api/automatizados/', include('automatizados.urls')), path('api/token/', admin_views.api_token, name='token_obtain_pair'), ] ``` --- ## 3. Crear y aplicar las migraciones Desde la carpeta `deployments/`: ``` docker-compose exec web python manage.py makemigrations productos docker-compose exec web python manage.py migrate ``` Para cargar las semillas manualmente sin reiniciar: ``` docker-compose exec web python manage.py loaddata semillas ``` --- ## 4. Probar con Hoppscotch Entra en **https://hoppscotch.io** (funciona en el navegador, sin instalar nada). ### 4.1 Importar la coleccion y los entornos En el repo `django-core-base` hay dos ficheros en la raiz: - `postman_collection.json` - las llamadas - `postman_environments.json` - los entornos (LOCAL, DEV, PROD) En Hoppscotch: 1. **Collections -> Import -> Postman** -> selecciona `postman_collection.json` 2. **Environments -> Import** -> selecciona `postman_environments.json` -> activa el entorno **LOCAL** ### 4.2 Obtener el token Ejecuta primero **Auth -> Obtener Token (Login)**. El script de la coleccion guarda el `access_token` automaticamente. ### 4.3 Probar `obtener` Crea una nueva llamada manual: - **Metodo:** POST - **URL:** `{{base_url}}/api/productos/obtener/` - **Header:** `Authorization: Bearer {{access_token}}` - **Body (JSON):** ```json { "id": 1, "activo": true } ``` Resultado esperado: datos del Producto Ejemplo A. ### 4.4 Probar `guardar` - **Metodo:** POST - **URL:** `{{base_url}}/api/productos/guardar/` - **Header:** `Authorization: Bearer {{access_token}}` - **Body (JSON):** ```json { "nombre": "Mi primer producto", "descripcion": "Creado desde Hoppscotch", "precio": 15.99 } ``` Resultado esperado: `{"id": 4, "mensaje": "Producto creado correctamente"}` --- ## 5. Subir los cambios > **Importante:** de momento trabajaras **unicamente en la rama `pre-dev`**. No toques `dev` ni `master` hasta que tengas mas experiencia con el flujo del proyecto. ### Con GitHub Desktop (recomendado para empezar) 1. Abre **GitHub Desktop** 2. En el menu superior: **File -> Add local repository** -> selecciona la carpeta `django-core-base` 3. En la barra de ramas (arriba a la izquierda) verifica que estas en **`pre-dev`**. Si no, haz clic en el desplegable y seleccionala. Si no aparece, haz clic en **Fetch origin** primero. 4. En el panel izquierdo veras todos los ficheros que has creado o modificado marcados en verde/amarillo 5. Abajo a la izquierda escribe el mensaje del commit: - **Summary:** `feat: crear app productos con endpoints obtener y guardar` 6. Haz clic en **Commit to pre-dev** 7. Haz clic en **Push origin** (boton azul arriba a la derecha) Listo. Los cambios estaran en la rama `pre-dev` del repositorio. ### Con CMD (alternativa) Si prefieres la terminal: ``` git checkout pre-dev git add . git commit -m "feat: crear app productos con endpoints obtener y guardar" git push origin pre-dev ``` --- ## 6. Verificar con HeidiSQL HeidiSQL es la herramienta para conectarse directamente a la base de datos PostgreSQL que corre dentro de Docker y ver los datos y los logs de tus llamadas. ### 6.1 Conectarse a la base de datos 1. Abre **HeidiSQL** 2. Haz clic en **Nueva** (esquina inferior izquierda) 3. Rellena los campos: - **Tipo de red:** PostgreSQL (TCP/IP) - **Host / IP:** `localhost` - **Puerto:** `5432` - **Usuario:** `django_user` - **Contrasena:** `django_password` - **Base de datos:** `django_test` 4. Haz clic en **Abrir** > Si no conecta, verifica que el contenedor esta corriendo con `docker-compose logs -f` desde la carpeta `deployments/`. --- ### 6.2 Ver los logs de tus endpoints Cada llamada que haces a la API queda registrada automaticamente en la tabla **`audit_logs`** gracias al LogService. Abre una nueva pestana de consulta en HeidiSQL (**Ctrl+T**) y ejecuta: ```sql SELECT id, "user", path, method, status_code, request, response, "createdAt" FROM audit_logs WHERE path LIKE '/productos/%' ORDER BY "createdAt" DESC LIMIT 20; ``` Veras una fila por cada bloque de LogService que se ejecuto. El campo `status_code` indica en que punto esta el log: | status_code | Significado | |---|---| | `0` | Inicio de la llamada (Bloque 1) | | `100` | Datos recibidos y limpiados (Bloque 2) | | `200` | Llamada completada con exito | | `400` | Error en los datos de entrada | | `500` | Error interno del servidor | Los campos `request` y `response` son JSON — HeidiSQL los muestra como texto, puedes copiarlos y pegarlos en cualquier visor JSON online para verlos bien formateados. --- ### 6.3 Ver los productos de la tabla Para ver todos los productos que hay en la base de datos: ```sql SELECT * FROM productos ORDER BY id DESC; ``` Para comprobar que el ultimo producto que insertaste con `guardar` llego correctamente: ```sql SELECT * FROM productos WHERE id = (SELECT MAX(id) FROM productos); ``` --- ### 6.4 Ver el log completo de una llamada concreta Si quieres ver exactamente que recibio y que respondio el endpoint `guardar` en cada llamada: ```sql SELECT id, path, status_code, request, response, "createdAt" FROM audit_logs WHERE path = '/productos/guardar/' ORDER BY "createdAt" DESC LIMIT 5; ``` Y para `obtener`: ```sql SELECT id, path, status_code, request, response, "createdAt" FROM audit_logs WHERE path = '/productos/obtener/' ORDER BY "createdAt" DESC LIMIT 5; ``` > **Truco:** si una llamada devolvio error 500, busca en `audit_logs` la fila con `status_code = '500'` de esa ruta — el campo `response` tendra el mensaje de error exacto que lanzo Python, lo que te ayudara a depurar sin tener que mirar los logs de Docker. --- ## Checklist de entrega - [ ] `app/productos/__init__.py` - [ ] `app/productos/apps.py` - [ ] `app/productos/models.py` con 5 campos - [ ] `app/productos/acciones.py` con `getData` y `setData` - [ ] `app/productos/views.py` con 4 bloques LogService en cada endpoint - [ ] `app/productos/urls.py` - [ ] `app/productos/fixtures/semillas.json` con 3 productos - [ ] `productos` en `INSTALLED_APPS` - [ ] `api/productos/` en `api_config/urls.py` - [ ] Migraciones creadas y aplicadas - [ ] Endpoint `obtener` funcionando en Hoppscotch - [ ] Endpoint `guardar` funcionando en Hoppscotch - [ ] Cambios subidos a `pre-dev` --- Cualquier duda, pregunta directamente. Animo!
nlarhziale was assigned by jjminguez 2026-04-17 21:24:54 +00:00
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: jjminguez/INFRAESTRUCTURA#2