🛠 Unidad ISS-05 · ProductType y Product — capa 🛠 CONSTRUIR
🧠 Comprender este bloque → · ✅ GATE de la unidad
Capa Página Para qué 🧠 Aprender Guía de estudio comprender, explicar y relacionar 🛠 Construir esta página ejecutar, programar y verificar ✅ GATE Condiciones de cierre condición para pasar al bloque siguiente Esta es la guía ejecutable. El cuerpo de abajo es el ISS técnico verbatim: comandos, rutas, versiones, verificaciones y criterios, sin simplificar.
ISS-05 — ProductType y Product
Objetivo
Modelar el catálogo y la integridad ProductType 1 → N Product.
Requisitos
- ISS-04 superado.
- Eliminar un tipo referenciado debe restringirse.
Construcción
apps/product/models.py ya existe. Se reescribe con los dos modelos. No hay marca, stock mínimo ni cantidad: esos campos no están en el diccionario de StoreLab.
cat > apps/product/models.py <<'EOF'
from decimal import Decimal
from django.core.validators import MinLengthValidator, MinValueValidator
from django.db import models
from apps.common.models import StoreLabModel
class ProductType(StoreLabModel):
name = models.CharField(max_length=100, unique=True, validators=[MinLengthValidator(2)])
description = models.CharField(max_length=255, null=True, blank=True)
class Meta:
db_table = "product_types"
verbose_name = "tipo de producto"
verbose_name_plural = "tipos de producto"
ordering = ["name", "id"]
constraints = [StoreLabModel.status_constraint("product_types_status_valid")]
def __str__(self):
return self.name
class Product(StoreLabModel):
product_type = models.ForeignKey(
ProductType,
on_delete=models.RESTRICT,
related_name="products",
db_column="product_type_id",
)
name = models.CharField(max_length=120, unique=True, validators=[MinLengthValidator(2)])
description = models.CharField(max_length=255, null=True, blank=True)
price = models.DecimalField(
max_digits=12,
decimal_places=2,
validators=[MinValueValidator(Decimal("0"))],
)
stock = models.PositiveIntegerField(default=0)
class Meta:
db_table = "products"
verbose_name = "producto"
verbose_name_plural = "productos"
ordering = ["name", "id"]
constraints = [
StoreLabModel.status_constraint("products_status_valid"),
models.CheckConstraint(
condition=models.Q(price__gte=0),
name="product_price_non_neg",
),
]
def __str__(self):
return self.name
EOF
python manage.py makemigrations product
python manage.py migrate
Explicación
RESTRICT impide borrar el padre mientras existan hijos. El test llama product_type.delete() y espera RestrictedError.
ProductType guarda name, description y status. Product guarda name, description, price, stock y la FK product_type.
Criterios de aceptación
- AC-05-01:
product_type_ides obligatorio. - AC-05-02: borrar el tipo con productos falla.
- AC-05-03: el nombre de producto es único.
- AC-05-04: status y stock por defecto son
inactivey 0.
Verificación
ProductApiTests.test_default_status_and_type_restriction y test_duplicate_product_name.
Evidencias
- EVI-05-01: migración
product.0001_initial. - EVI-05-02:
RestrictedErrorobservado por el test.
GATE
| AC | Verificación | Evidencia | Resultado |
|---|---|---|---|
| AC-05-01 | FK sin null | modelo | PASS |
| AC-05-02 | delete del tipo | test | PASS |
| AC-05-03 | nombre duplicado = 400 | test | PASS |
| AC-05-04 | defaults | test | PASS |
✅ GATE de la unidad ISS-05 — este bloque no añade ningún criterio nuevo.
Las condiciones de cierre son las de esta misma página:
- 📋 Criterios de aceptación → Criterios de aceptación
- ✅ GATE → GATE
- 🔎 Verificación → Verificación
- 🧠 Autoevaluación → Evaluación del cuaderno
Con el GATE en verde queda habilitado el bloque siguiente de la ruta.
Navegación de la ruta: ← ISS-05 · 🧠 Aprender · ↑ Ruta Django · → ISS-06 · 🧠 Aprender