Saltar a contenido

🛠 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.

ProductType 1 ───────── N Product

Criterios de aceptación

  • AC-05-01: product_type_id es 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 inactive y 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: RestrictedError observado 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:

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