Mmcp.market

django-patterns skill

by affaan-m·affaan-m/ECC·269k stars·MIT

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.

A100/100content scan

Is the django-patterns skill safe?

Clean: nothing in its files matched our rules. We read 1 file in the folder on 2026-09-28.

No findings.

Install the django-patterns skill

A skill is a folder. Copy it into your agent's skills folder and the agent loads it when the task matches its description.

git clone --depth 1 https://github.com/affaan-m/ECC.git /tmp/ECC
mkdir -p ~/.claude/skills
cp -r /tmp/ECC/docs/es/skills/django-patterns ~/.claude/skills/django-patterns
available in every project

In the Claude apps, zip the folder and upload it from the Skills settings. The folder on GitHub

The instructions your agent would load

SKILL.md as published, without the frontmatter. Read it on GitHub

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

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 

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 ProductCreateSerial

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_

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()
  

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']),

More skills from affaan-m/ECC

  • AaccessibilityWCAG 2.2 レベル AA 標準を用いてインクルーシブなデジタルプロダクトを設計・実装・監査します。Web 用のセマンティック ARIA および Web・ネイティブプラットフォーム(iOS/Android)のアクセシビリティトレイトを生成するために使用します。
  • Aagent-architecture-auditエージェントおよび LLM アプリケーション向けのフルスタック診断。12 層のエージェントスタックにおけるラッパーリグレッション、メモリ汚染、ツール規律の失敗、隠れた修復ループ、レンダリング破損を監査します。重要度順の発見事項とコードファーストの修正を生成します。エージェントアプリケーション、自律ループ、または LLM を活用した機能を構築する開発者に必須です。
  • Aagent-evalカスタムタスクでコーディングエージェント(Claude Code、Aider、Codex など)をヘッドツーヘッドで比較し、合格率、コスト、時間、一貫性のメトリクスを測定します
  • Aagent-harness-constructionAI エージェントのアクション空間、ツール定義、観測フォーマットを設計・最適化して完了率を向上させます。
  • Aagent-introspection-debuggingStructured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.
  • Aagent-introspection-debuggingキャプチャ、診断、封じ込め回復、内省レポートを使用した AI エージェント障害のための構造化された自己デバッグワークフロー。
  • Aagent-payment-x402タスクごとのバジェット、支出コントロール、ノンカストディアルウォレットを備えた x402 決済実行を AI エージェントに追加します。agentwallet-sdk を通じて Base をサポートし、OKX Payments / OKX エージェント決済プロトコルを通じて X Layer をサポートします。
  • Aagent-sortBuild an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.
  • Aagent-sort並行リポジトリ対応のレビューパスを使用して、スキル、コマンド、ルール、フック、エクストラを DAILY と LIBRARY のバケットに分類することで、特定のリポジトリ向けのエビデンスに基づいた ECC インストール計画を構築します。プロジェクトが完全なバンドルをロードする代わりに実際に必要なものに ECC をトリミングする必要がある場合に使用します。
  • Aagentic-engineeringOperate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when AI agents perform most implementation work and humans enforce quality and risk controls.
  • Aagentic-engineering評価ファースト実行、分解、コスト対応モデルルーティングを使用してエージェニックエンジニアとして動作します。
  • Aagentic-osClaude Code 上に永続的なマルチエージェントオペレーティングシステムを構築します。カーネルアーキテクチャ、スペシャリストエージェント、スラッシュコマンド、ファイルベースのメモリ、スケジュールされた自動化、外部データベースなしの状態管理をカバーします。

All agent skills → · MCP servers