Skip to main content

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.

معلومات المصدر

المستودع
affaan-m/ECC
آخر نشاط في المصدر
٧ يونيو ٢٠٢٦ في ٠٥:٢٦
لغة SKILL.md المكتشفة
الإسبانية
النجوم
٢٧٦٬٥٤٩
التفرعات
٤١٬٢٤٣

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
django-patterns
description
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.
origin
ECC
# 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 ```python # 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 ```python 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 ```python 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 ```python 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 ```python 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 ```python 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)
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub