Source profileQuality 73/100

affaan-m/ECC/docs/es/skills/django-patterns/SKILL.md

django-patterns

Patrones de arquitectura Django, diseño de API REST con DRF, buenas prácticas de ORM, caché, señales, middleware y aplicaciones Django de nivel producción.

Source repository stars
234,327
Declared platforms
0
Static risk flags
0
Last source update
2026-07-27
Source checked
2026-07-28

Decision brief

What it does—and where it fits

Patrones de arquitectura Django de nivel producción para aplicaciones escalables y mantenibles.

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "docs/es/skills/django-patterns"
    Safe inspection promptEditorial

    Inspect the Agent Skill "django-patterns" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/docs/es/skills/django-patterns/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      Cuándo Activar

      Construir aplicaciones web Django

      Construir aplicaciones web DjangoDiseñar APIs con Django REST FrameworkTrabajar con el ORM de Django y modelos
    2. 02

      Estructura del Proyecto

      Review the “Estructura del Proyecto” section in the pinned source before continuing.

      Review and apply the “Estructura del Proyecto” source section.
    3. 03

      Layout Recomendado

      Review the “Layout Recomendado” section in the pinned source before continuing.

      Review and apply the “Layout Recomendado” source section.
    4. 04

      Patrón de Configuración Dividida

      Review the “Patrón de Configuración Dividida” section in the pinned source before continuing.

      Review and apply the “Patrón de Configuración Dividida” source section.
    5. 05

      config/settings/base.py

      from pathlib import Path

      from pathlib import PathBASEDIR = Path(file).resolve().parent.parent.parentSECRETKEY = env('DJANGOSECRETKEY') DEBUG = False ALLOWEDHOSTS = []

    Permission review

    Static risk signals and limitations

    No configured static risk pattern was detected

    This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score73/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    docs/es/skills/django-patterns/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    Patrones de Desarrollo Django

    Patrones de arquitectura Django de nivel producción para aplicaciones escalables y mantenibles.

    Cuándo Activar

    • Construir aplicaciones web Django
    • Diseñar APIs con Django REST Framework
    • Trabajar con el ORM de Django y modelos
    • Configurar la estructura del proyecto Django
    • Implementar caché, señales, middleware

    Estructura del Proyecto

    Layout Recomendado

    myproject/
    ├── config/
    │   ├── __init__.py
    │   ├── settings/
    │   │   ├── __init__.py
    │   │   ├── base.py          # Configuración base
    │   │   ├── development.py   # Configuración de desarrollo
    │   │   ├── production.py    # Configuración de producción
    │   │   └── test.py          # Configuración de pruebas
    │   ├── urls.py
    │   ├── wsgi.py
    │   └── asgi.py
    ├── manage.py
    └── apps/
        ├── __init__.py
        ├── users/
        │   ├── __init__.py
        │   ├── models.py
        │   ├── views.py
        │   ├── serializers.py
        │   ├── urls.py
        │   ├── permissions.py
        │   ├── filters.py
        │   ├── services.py
        │   └── tests/
        └── products/
            └── ...
    

    Patrón de Configuración Dividida

    # config/settings/base.py
    from pathlib import Path
    
    BASE_DIR = Path(__file__).resolve().parent.parent.parent
    
    SECRET_KEY = env('DJANGO_SECRET_KEY')
    DEBUG = False
    ALLOWED_HOSTS = []
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'rest_framework',
        'rest_framework.authtoken',
        'corsheaders',
        # Apps locales
        'apps.users',
        'apps.products',
    ]
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'whitenoise.middleware.WhiteNoiseMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'corsheaders.middleware.CorsMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
    ROOT_URLCONF = 'config.urls'
    WSGI_APPLICATION = 'config.wsgi.application'
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': env('DB_NAME'),
            'USER': env('DB_USER'),
            'PASSWORD': env('DB_PASSWORD'),
            'HOST': env('DB_HOST'),
            'PORT': env('DB_PORT', default='5432'),
        }
    }
    
    # config/settings/development.py
    from .base import *
    
    DEBUG = True
    ALLOWED_HOSTS = ['localhost', '127.0.0.1']
    
    DATABASES['default']['NAME'] = 'myproject_dev'
    
    INSTALLED_APPS += ['debug_toolbar']
    
    MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware']
    
    EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
    
    # config/settings/production.py
    from .base import *
    
    DEBUG = False
    ALLOWED_HOSTS = env.list('ALLOWED_HOSTS')
    SECURE_SSL_REDIRECT = True
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True
    SECURE_HSTS_SECONDS = 31536000
    SECURE_HSTS_INCLUDE_SUBDOMAINS = True
    SECURE_HSTS_PRELOAD = True
    
    # Logging
    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'handlers': {
            'file': {
                'level': 'WARNING',
                'class': 'logging.FileHandler',
                'filename': '/var/log/django/django.log',
            },
        },
        'loggers': {
            'django': {
                'handlers': ['file'],
                'level': 'WARNING',
                'propagate': True,
            },
        },
    }
    

    Patrones de Diseño de Modelos

    Buenas Prácticas de Modelos

    from django.db import models
    from django.contrib.auth.models import AbstractUser
    from django.core.validators import MinValueValidator, MaxValueValidator
    
    class User(AbstractUser):
        """Modelo de usuario personalizado que extiende AbstractUser."""
        email = models.EmailField(unique=True)
        phone = models.CharField(max_length=20, blank=True)
        birth_date = models.DateField(null=True, blank=True)
    
        USERNAME_FIELD = 'email'
        REQUIRED_FIELDS = ['username']
    
        class Meta:
            db_table = 'users'
            verbose_name = 'user'
            verbose_name_plural = 'users'
            ordering = ['-date_joined']
    
        def __str__(self):
            return self.email
    
        def get_full_name(self):
            return f"{self.first_name} {self.last_name}".strip()
    
    class Product(models.Model):
        """Modelo de producto con configuración de campos apropiada."""
        name = models.CharField(max_length=200)
        slug = models.SlugField(unique=True, max_length=250)
        description = models.TextField(blank=True)
        price = models.DecimalField(
            max_digits=10,
            decimal_places=2,
            validators=[MinValueValidator(0)]
        )
        stock = models.PositiveIntegerField(default=0)
        is_active = models.BooleanField(default=True)
        category = models.ForeignKey(
            'Category',
            on_delete=models.CASCADE,
            related_name='products'
        )
        tags = models.ManyToManyField('Tag', blank=True, related_name='products')
        created_at = models.DateTimeField(auto_now_add=True)
        updated_at = models.DateTimeField(auto_now=True)
    
        class Meta:
            db_table = 'products'
            ordering = ['-created_at']
            indexes = [
                models.Index(fields=['slug']),
                models.Index(fields=['-created_at']),
                models.Index(fields=['category', 'is_active']),
            ]
            constraints = [
                models.CheckConstraint(
                    check=models.Q(price__gte=0),
                    name='price_non_negative'
                )
            ]
    
        def __str__(self):
            return self.name
    
        def save(self, *args, **kwargs):
            if not self.slug:
                self.slug = slugify(self.name)
            super().save(*args, **kwargs)
    

    Buenas Prácticas de QuerySet

    from django.db import models
    
    class ProductQuerySet(models.QuerySet):
        """QuerySet personalizado para el modelo Product."""
    
        def active(self):
            """Retornar solo productos activos."""
            return self.filter(is_active=True)
    
        def with_category(self):
            """Seleccionar categoría relacionada para evitar consultas N+1."""
            return self.select_related('category')
    
        def with_tags(self):
            """Prefetch tags para relación muchos-a-muchos."""
            return self.prefetch_related('tags')
    
        def in_stock(self):
            """Retornar productos con stock > 0."""
            return self.filter(stock__gt=0)
    
        def search(self, query):
            """Buscar productos por nombre o descripción."""
            return self.filter(
                models.Q(name__icontains=query) |
                models.Q(description__icontains=query)
            )
    
    class Product(models.Model):
        # ... campos ...
    
        objects = ProductQuerySet.as_manager()  # Usar QuerySet personalizado
    
    # Uso
    Product.objects.active().with_category().in_stock()
    

    Métodos de Manager

    class ProductManager(models.Manager):
        """Manager personalizado para consultas complejas."""
    
        def get_or_none(self, **kwargs):
            """Retornar objeto o None en lugar de DoesNotExist."""
            try:
                return self.get(**kwargs)
            except self.model.DoesNotExist:
                return None
    
        def create_with_tags(self, name, price, tag_names):
            """Crear producto con tags asociados."""
            product = self.create(name=name, price=price)
            tags = [Tag.objects.get_or_create(name=name)[0] for name in tag_names]
            product.tags.set(tags)
            return product
    
        def bulk_update_stock(self, product_ids, quantity):
            """Actualización masiva de stock para múltiples productos."""
            return self.filter(id__in=product_ids).update(stock=quantity)
    
    # En el modelo
    class Product(models.Model):
        # ... campos ...
        custom = ProductManager()
    

    Patrones de Django REST Framework

    Patrones de Serializer

    from rest_framework import serializers
    from django.contrib.auth.password_validation import validate_password
    from .models import Product, User
    
    class ProductSerializer(serializers.ModelSerializer):
        """Serializer para el modelo Product."""
    
        category_name = serializers.CharField(source='category.name', read_only=True)
        average_rating = serializers.FloatField(read_only=True)
        discount_price = serializers.SerializerMethodField()
    
        class Meta:
            model = Product
            fields = [
                'id', 'name', 'slug', 'description', 'price',
                'discount_price', 'stock', 'category_name',
                'average_rating', 'created_at'
            ]
            read_only_fields = ['id', 'slug', 'created_at']
    
        def get_discount_price(self, obj):
            """Calcular precio con descuento si aplica."""
            if hasattr(obj, 'discount') and obj.discount:
                return obj.price * (1 - obj.discount.percent / 100)
            return obj.price
    
        def validate_price(self, value):
            """Asegurar que el precio no sea negativo."""
            if value < 0:
                raise serializers.ValidationError("Price cannot be negative.")
            return value
    
    class ProductCreateSerializer(serializers.ModelSerializer):
        """Serializer para crear productos."""
    
        class Meta:
            model = Product
            fields = ['name', 'description', 'price', 'stock', 'category']
    
        def validate(self, data):
            """Validación personalizada para múltiples campos."""
            if data['price'] > 10000 and data['stock'] > 100:
                raise serializers.ValidationError(
                    "Cannot have high-value products with large stock."
                )
            return data
    
    class UserRegistrationSerializer(serializers.ModelSerializer):
        """Serializer para registro de usuario."""
    
        password = serializers.CharField(
            write_only=True,
            required=True,
            validators=[validate_password],
            style={'input_type': 'password'}
        )
        password_confirm = serializers.CharField(write_only=True, style={'input_type': 'password'})
    
        class Meta:
            model = User
            fields = ['email', 'username', 'password', 'password_confirm']
    
        def validate(self, data):
            """Validar que las contraseñas coincidan."""
            if data['password'] != data['password_confirm']:
                raise serializers.ValidationError({
                    "password_confirm": "Password fields didn't match."
                })
            return data
    
        def create(self, validated_data):
            """Crear usuario con contraseña hasheada."""
            validated_data.pop('password_confirm')
            password = validated_data.pop('password')
            user = User.objects.create(**validated_data)
            user.set_password(password)
            user.save()
            return user
    

    Patrones de ViewSet

    from rest_framework import viewsets, status, filters
    from rest_framework.decorators import action
    from rest_framework.response import Response
    from rest_framework.permissions import IsAuthenticated, IsAdminUser
    from django_filters.rest_framework import DjangoFilterBackend
    from .models import Product
    from .serializers import ProductSerializer, ProductCreateSerializer
    from .permissions import IsOwnerOrReadOnly
    from .filters import ProductFilter
    from .services import ProductService
    
    class ProductViewSet(viewsets.ModelViewSet):
        """ViewSet para el modelo Product."""
    
        queryset = Product.objects.select_related('category').prefetch_related('tags')
        permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
        filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
        filterset_class = ProductFilter
        search_fields = ['name', 'description']
        ordering_fields = ['price', 'created_at', 'name']
        ordering = ['-created_at']
    
        def get_serializer_class(self):
            """Retornar serializer apropiado según la acción."""
            if self.action == 'create':
                return ProductCreateSerializer
            return ProductSerializer
    
        def perform_create(self, serializer):
            """Guardar con contexto de usuario."""
            serializer.save(created_by=self.request.user)
    
        @action(detail=False, methods=['get'])
        def featured(self, request):
            """Retornar productos destacados."""
            featured = self.queryset.filter(is_featured=True)[:10]
            serializer = self.get_serializer(featured, many=True)
            return Response(serializer.data)
    
        @action(detail=True, methods=['post'])
        def purchase(self, request, pk=None):
            """Comprar un producto."""
            product = self.get_object()
            service = ProductService()
            result = service.purchase(product, request.user)
            return Response(result, status=status.HTTP_201_CREATED)
    
        @action(detail=False, methods=['get'], permission_classes=[IsAuthenticated])
        def my_products(self, request):
            """Retornar productos creados por el usuario actual."""
            products = self.queryset.filter(created_by=request.user)
            page = self.paginate_queryset(products)
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
    

    Acciones Personalizadas

    from rest_framework.decorators import api_view, permission_classes
    from rest_framework.permissions import IsAuthenticated
    from rest_framework.response import Response
    
    @api_view(['POST'])
    @permission_classes([IsAuthenticated])
    def add_to_cart(request):
        """Agregar producto al carrito del usuario."""
        product_id = request.data.get('product_id')
        quantity = request.data.get('quantity', 1)
    
        try:
            product = Product.objects.get(id=product_id)
        except Product.DoesNotExist:
            return Response(
                {'error': 'Product not found'},
                status=status.HTTP_404_NOT_FOUND
            )
    
        cart, _ = Cart.objects.get_or_create(user=request.user)
        CartItem.objects.create(
            cart=cart,
            product=product,
            quantity=quantity
        )
    
        return Response({'message': 'Added to cart'}, status=status.HTTP_201_CREATED)
    

    Patrón de Capa de Servicio

    # apps/orders/services.py
    from typing import Optional
    from django.db import transaction
    from .models import Order, OrderItem
    
    class OrderService:
        """Capa de servicio para la lógica de negocio relacionada con pedidos."""
    
        @staticmethod
        @transaction.atomic
        def create_order(user, cart: Cart) -> Order:
            """Crear pedido a partir del carrito."""
            order = Order.objects.create(
                user=user,
                total_price=cart.total_price
            )
    
            for item in cart.items.all():
                OrderItem.objects.create(
                    order=order,
                    product=item.product,
                    quantity=item.quantity,
                    price=item.product.price
                )
    
            # Vaciar carrito
            cart.items.all().delete()
    
            return order
    
        @staticmethod
        def process_payment(order: Order, payment_data: dict) -> bool:
            """Procesar el pago del pedido."""
            # Integración con pasarela de pago
            payment = PaymentGateway.charge(
                amount=order.total_price,
                token=payment_data['token']
            )
    
            if payment.success:
                order.status = Order.Status.PAID
                order.save()
                # Enviar email de confirmación
                OrderService.send_confirmation_email(order)
                return True
    
            return False
    
        @staticmethod
        def send_confirmation_email(order: Order):
            """Enviar email de confirmación del pedido."""
            # Lógica de envío de email
            pass
    

    Estrategias de Caché

    Caché a Nivel de Vista

    from django.views.decorators.cache import cache_page
    from django.utils.decorators import method_decorator
    
    @method_decorator(cache_page(60 * 15), name='dispatch')  # 15 minutos
    class ProductListView(generic.ListView):
        model = Product
        template_name = 'products/list.html'
        context_object_name = 'products'
    

    Caché de Fragmentos de Plantilla

    {% load cache %}
    {% cache 500 sidebar %}
        ... contenido costoso del sidebar ...
    {% endcache %}
    

    Caché de Bajo Nivel

    from django.core.cache import cache
    
    def get_featured_products():
        """Obtener productos destacados con caché."""
        cache_key = 'featured_products'
        products = cache.get(cache_key)
    
        if products is None:
            products = list(Product.objects.filter(is_featured=True))
            cache.set(cache_key, products, timeout=60 * 15)  # 15 minutos
    
        return products
    

    Caché de QuerySet

    from django.core.cache import cache
    
    def get_popular_categories():
        cache_key = 'popular_categories'
        categories = cache.get(cache_key)
    
        if categories is None:
            categories = list(Category.objects.annotate(
                product_count=Count('products')
            ).filter(product_count__gt=10).order_by('-product_count')[:20])
            cache.set(cache_key, categories, timeout=60 * 60)  # 1 hora
    
        return categories
    

    Señales

    Patrones de Señal

    # apps/users/signals.py
    from django.db.models.signals import post_save
    from django.dispatch import receiver
    from django.contrib.auth import get_user_model
    from .models import Profile
    
    User = get_user_model()
    
    @receiver(post_save, sender=User)
    def create_user_profile(sender, instance, created, **kwargs):
        """Crear perfil cuando se crea el usuario."""
        if created:
            Profile.objects.create(user=instance)
    
    @receiver(post_save, sender=User)
    def save_user_profile(sender, instance, **kwargs):
        """Guardar perfil cuando se guarda el usuario."""
        instance.profile.save()
    
    # apps/users/apps.py
    from django.apps import AppConfig
    
    class UsersConfig(AppConfig):
        default_auto_field = 'django.db.models.BigAutoField'
        name = 'apps.users'
    
        def ready(self):
            """Importar señales cuando la app esté lista."""
            import apps.users.signals
    

    Middleware

    Middleware Personalizado

    # middleware/active_user_middleware.py
    import time
    from django.utils.deprecation import MiddlewareMixin
    
    class ActiveUserMiddleware(MiddlewareMixin):
        """Middleware para rastrear usuarios activos."""
    
        def process_request(self, request):
            """Procesar request entrante."""
            if request.user.is_authenticated:
                # Actualizar última hora activa
                request.user.last_active = timezone.now()
                request.user.save(update_fields=['last_active'])
    
    class RequestLoggingMiddleware(MiddlewareMixin):
        """Middleware para logging de requests."""
    
        def process_request(self, request):
            """Registrar hora de inicio del request."""
            request.start_time = time.time()
    
        def process_response(self, request, response):
            """Registrar duración del request."""
            if hasattr(request, 'start_time'):
                duration = time.time() - request.start_time
                logger.info(f'{request.method} {request.path} - {response.status_code} - {duration:.3f}s')
            return response
    

    Optimización de Rendimiento

    Prevención de Consultas N+1

    # Mal - consultas N+1
    products = Product.objects.all()
    for product in products:
        print(product.category.name)  # Consulta separada para cada producto
    
    # Bien - consulta única con select_related
    products = Product.objects.select_related('category').all()
    for product in products:
        print(product.category.name)
    
    # Bien - Prefetch para muchos-a-muchos
    products = Product.objects.prefetch_related('tags').all()
    for product in products:
        for tag in product.tags.all():
            print(tag.name)
    

    Indexación de Base de Datos

    class Product(models.Model):
        name = models.CharField(max_length=200, db_index=True)
        slug = models.SlugField(unique=True)
        category = models.ForeignKey('Category', on_delete=models.CASCADE)
        created_at = models.DateTimeField(auto_now_add=True)
    
        class Meta:
            indexes = [
                models.Index(fields=['name']),
                models.Index(fields=['-created_at']),
                models.Index(fields=['category', 'created_at']),
            ]
    

    Operaciones Masivas

    # Creación masiva
    Product.objects.bulk_create([
        Product(name=f'Product {i}', price=10.00)
        for i in range(1000)
    ])
    
    # Actualización masiva
    products = Product.objects.all()[:100]
    for product in products:
        product.is_active = True
    Product.objects.bulk_update(products, ['is_active'])
    
    # Eliminación masiva
    Product.objects.filter(stock=0).delete()
    

    Referencia Rápida

    PatrónDescripción
    Configuración divididaSeparar configuración de dev/prod/test
    QuerySet personalizadoMétodos de consulta reutilizables
    Capa de ServicioSeparación de lógica de negocio
    ViewSetEndpoints de API REST
    Validación de SerializerTransformación de request/response
    select_relatedOptimización de clave foránea
    prefetch_relatedOptimización de muchos-a-muchos
    Cache firstCachear operaciones costosas
    SeñalesAcciones basadas en eventos
    MiddlewareProcesamiento de request/response

    Recuerda: Django proporciona muchos atajos, pero para aplicaciones de producción, la estructura y organización importan más que el código conciso. Construir para la mantenibilidad.

    Alternatives

    Compare before choosing