Claude Code Plugins

Community-maintained marketplace

Feedback

Expert in Django web framework for building robust Python applications. Use when working with Django, Django REST Framework, Django ORM, models, views, serializers, middleware, or when the user mentions Django, Python web development, or DRF.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name Django Expert
description Expert in Django web framework for building robust Python applications. Use when working with Django, Django REST Framework, Django ORM, models, views, serializers, middleware, or when the user mentions Django, Python web development, or DRF.

Django Expert

A specialized skill for building production-ready web applications with Django and Django REST Framework. This skill covers MVC architecture, ORM, REST APIs, authentication, and Django best practices.

Instructions

Core Workflow

  1. Understand requirements

    • Identify if this is a traditional Django app or REST API (DRF)
    • Determine database requirements
    • Understand authentication needs
    • Identify third-party integrations
  2. Project structure

    • Follow Django app-based architecture
    • Separate business logic from views
    • Use proper settings management (dev/prod)
    • Implement custom user models if needed
  3. Implement features

    • Create models with proper relationships
    • Implement views/viewsets following best practices
    • Create serializers with proper validation (DRF)
    • Add authentication and permissions
    • Implement middleware and signals where appropriate
  4. Testing and documentation

    • Write tests using Django's test framework
    • Document APIs with drf-spectacular
    • Configure for production deployment

Django Project Structure

myproject/
├── manage.py
├── myproject/              # Project root
│   ├── __init__.py
│   ├── settings/          # Split settings
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── development.py
│   │   └── production.py
│   ├── urls.py
│   └── wsgi.py
├── apps/                   # All Django apps
│   ├── users/
│   │   ├── models.py
│   │   ├── views.py
│   │   ├── serializers.py  # DRF
│   │   ├── urls.py
│   │   ├── admin.py
│   │   ├── tests/
│   │   └── managers.py
│   └── products/
└── requirements/
    ├── base.txt
    ├── development.txt
    └── production.txt

Model Best Practices

from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
from django.contrib.auth.models import AbstractUser

# Custom User Model
class User(AbstractUser):
    """Custom user model with additional fields"""
    email = models.EmailField(unique=True)
    bio = models.TextField(max_length=500, blank=True)
    birth_date = models.DateField(null=True, blank=True)
    avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']

    class Meta:
        ordering = ['-date_joined']
        indexes = [
            models.Index(fields=['email']),
        ]

    def __str__(self):
        return self.email

# Model with relationships
class Product(models.Model):
    """Product model with proper field types and validation"""
    name = models.CharField(max_length=200, db_index=True)
    slug = models.SlugField(max_length=200, unique=True)
    description = models.TextField()
    price = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0)]
    )
    stock = models.PositiveIntegerField(default=0)
    available = models.BooleanField(default=True)
    created_by = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='products'
    )
    category = models.ForeignKey(
        'Category',
        on_delete=models.SET_NULL,
        null=True,
        related_name='products'
    )
    tags = models.ManyToManyField('Tag', blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['slug']),
            models.Index(fields=['-created_at']),
        ]

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        from django.urls import reverse
        return reverse('product-detail', kwargs={'slug': self.slug})

    def is_in_stock(self):
        return self.available and self.stock > 0

# Custom Manager
class PublishedManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(status='published')

class Post(models.Model):
    STATUS_CHOICES = [
        ('draft', 'Draft'),
        ('published', 'Published'),
    ]

    title = models.CharField(max_length=200)
    content = models.TextField()
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')

    objects = models.Manager()  # Default manager
    published = PublishedManager()  # Custom manager

Django REST Framework

# serializers.py
from rest_framework import serializers
from .models import Product, Category

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = ['id', 'name', 'slug']

class ProductSerializer(serializers.ModelSerializer):
    category = CategorySerializer(read_only=True)
    category_id = serializers.PrimaryKeyRelatedField(
        queryset=Category.objects.all(),
        source='category',
        write_only=True
    )
    created_by_email = serializers.EmailField(source='created_by.email', read_only=True)

    class Meta:
        model = Product
        fields = [
            'id', 'name', 'slug', 'description', 'price', 'stock',
            'available', 'category', 'category_id', 'created_by_email',
            'created_at', 'updated_at'
        ]
        read_only_fields = ['slug', 'created_at', 'updated_at']

    def validate_price(self, value):
        if value < 0:
            raise serializers.ValidationError("Price cannot be negative")
        return value

    def validate(self, data):
        # Cross-field validation
        if data.get('available') and data.get('stock', 0) == 0:
            raise serializers.ValidationError(
                "Product cannot be available with zero stock"
            )
        return data

# views.py (ViewSets)
from rest_framework import viewsets, filters, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
from django_filters.rest_framework import DjangoFilterBackend

class ProductViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing products.

    list: Get all products
    retrieve: Get a single product
    create: Create a new product
    update: Update a product
    destroy: Delete a product
    """
    queryset = Product.objects.select_related('category', 'created_by').all()
    serializer_class = ProductSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_fields = ['category', 'available']
    search_fields = ['name', 'description']
    ordering_fields = ['price', 'created_at']
    lookup_field = 'slug'

    def perform_create(self, serializer):
        serializer.save(created_by=self.request.user)

    @action(detail=True, methods=['post'])
    def purchase(self, request, slug=None):
        """Custom action to purchase a product"""
        product = self.get_object()
        quantity = request.data.get('quantity', 1)

        if not product.is_in_stock():
            return Response(
                {'error': 'Product out of stock'},
                status=status.HTTP_400_BAD_REQUEST
            )

        if product.stock < quantity:
            return Response(
                {'error': f'Only {product.stock} items available'},
                status=status.HTTP_400_BAD_REQUEST
            )

        # Process purchase logic here
        product.stock -= quantity
        product.save()

        return Response({'message': 'Purchase successful'})

    @action(detail=False, methods=['get'])
    def low_stock(self, request):
        """Get products with low stock"""
        low_stock_products = self.queryset.filter(stock__lt=10, available=True)
        serializer = self.get_serializer(low_stock_products, many=True)
        return Response(serializer.data)

# urls.py
from rest_framework.routers import DefaultRouter
from .views import ProductViewSet

router = DefaultRouter()
router.register(r'products', ProductViewSet, basename='product')

urlpatterns = router.urls

Authentication & Permissions

# Custom Permission
from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    Custom permission to only allow owners of an object to edit it.
    """
    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request
        if request.method in permissions.SAFE_METHODS:
            return True

        # Write permissions are only allowed to the owner
        return obj.created_by == request.user

# JWT Authentication (using Simple JWT)
# settings.py
from datetime import timedelta

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticatedOrReadOnly',
    ],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10,
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
    ],
}

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
    'ROTATE_REFRESH_TOKENS': True,
    'BLACKLIST_AFTER_ROTATION': True,
}

# views.py - Auth endpoints
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)
        # Add custom claims
        token['email'] = user.email
        token['is_staff'] = user.is_staff
        return token

class CustomTokenObtainPairView(TokenObtainPairView):
    serializer_class = CustomTokenObtainPairSerializer

Middleware & Signals

# middleware.py
import logging

logger = logging.getLogger(__name__)

class RequestLoggingMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # Log request
        logger.info(f"{request.method} {request.path}")

        response = self.get_response(request)

        # Log response
        logger.info(f"{request.method} {request.path} - {response.status_code}")

        return response

# signals.py
from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
from .models import Product

@receiver(post_save, sender=Product)
def product_saved(sender, instance, created, **kwargs):
    if created:
        # Send notification or perform action when product is created
        logger.info(f"New product created: {instance.name}")
    else:
        # Product was updated
        logger.info(f"Product updated: {instance.name}")

@receiver(pre_delete, sender=Product)
def product_deleted(sender, instance, **kwargs):
    # Cleanup before deletion
    if instance.avatar:
        instance.avatar.delete(save=False)

Testing

# tests/test_models.py
from django.test import TestCase
from django.contrib.auth import get_user_model
from apps.products.models import Product, Category

User = get_user_model()

class ProductModelTest(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(
            email='test@example.com',
            username='testuser',
            password='testpass123'
        )
        self.category = Category.objects.create(name='Electronics', slug='electronics')
        self.product = Product.objects.create(
            name='Laptop',
            slug='laptop',
            description='A great laptop',
            price=999.99,
            stock=10,
            created_by=self.user,
            category=self.category
        )

    def test_product_creation(self):
        self.assertEqual(self.product.name, 'Laptop')
        self.assertEqual(str(self.product), 'Laptop')

    def test_is_in_stock(self):
        self.assertTrue(self.product.is_in_stock())

        self.product.stock = 0
        self.assertFalse(self.product.is_in_stock())

# tests/test_api.py
from rest_framework.test import APITestCase, APIClient
from rest_framework import status
from django.urls import reverse

class ProductAPITest(APITestCase):
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(
            email='test@example.com',
            username='testuser',
            password='testpass123'
        )
        self.category = Category.objects.create(name='Electronics', slug='electronics')

    def test_create_product_authenticated(self):
        self.client.force_authenticate(user=self.user)

        url = reverse('product-list')
        data = {
            'name': 'New Product',
            'description': 'Product description',
            'price': 99.99,
            'stock': 50,
            'category_id': self.category.id
        }

        response = self.client.post(url, data)

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(Product.objects.count(), 1)
        self.assertEqual(Product.objects.first().name, 'New Product')

    def test_list_products(self):
        Product.objects.create(
            name='Product 1',
            slug='product-1',
            description='Description',
            price=50.00,
            stock=10,
            created_by=self.user,
            category=self.category
        )

        url = reverse('product-list')
        response = self.client.get(url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)

Performance Optimization

# Use select_related and prefetch_related
# Bad
products = Product.objects.all()
for product in products:
    print(product.category.name)  # N+1 query problem

# Good
products = Product.objects.select_related('category', 'created_by').all()
for product in products:
    print(product.category.name)  # Single query

# For many-to-many
products = Product.objects.prefetch_related('tags').all()

# Database indexes in models
class Meta:
    indexes = [
        models.Index(fields=['slug']),
        models.Index(fields=['-created_at']),
        models.Index(fields=['category', '-created_at']),
    ]

# Caching with Redis
from django.core.cache import cache
from django.views.decorators.cache import cache_page

@cache_page(60 * 15)  # Cache for 15 minutes
def product_list(request):
    products = Product.objects.all()
    return render(request, 'products/list.html', {'products': products})

# Cache querysets
def get_products():
    products = cache.get('all_products')
    if products is None:
        products = list(Product.objects.select_related('category').all())
        cache.set('all_products', products, 60 * 15)
    return products

Critical Rules

Always Do

  • Use environment variables for sensitive data
  • Implement proper model validation
  • Use select_related/prefetch_related to avoid N+1 queries
  • Write tests for models, views, and APIs
  • Use database indexes for frequently queried fields
  • Implement proper authentication and permissions
  • Document APIs with drf-spectacular
  • Use Django's ORM efficiently
  • Handle errors gracefully with proper HTTP status codes

Never Do

  • Never store passwords in plain text
  • Never expose Django secret key
  • Never use raw SQL unless absolutely necessary
  • Never skip migrations
  • Never commit sensitive data
  • Never trust user input without validation
  • Never use DEBUG=True in production
  • Never ignore database optimization

Knowledge Base

  • Django Core: Models, Views, Templates, Forms, Admin
  • Django REST Framework: Serializers, ViewSets, Permissions, Authentication
  • ORM: QuerySets, Managers, Aggregation, F/Q expressions
  • Authentication: JWT, Session, OAuth
  • Testing: Django TestCase, APITestCase
  • Performance: Query optimization, Caching, Database indexing
  • Security: CSRF, XSS, SQL injection prevention
  • Deployment: WSGI, Gunicorn, Nginx, Docker

Integration with Other Skills

  • Works with: Fullstack Guardian, Test Master, DevOps Engineer
  • Complements: FastAPI Expert (alternative Python framework), Code Documenter

Best Practices Summary

  1. Models: Use proper field types, validators, indexes
  2. Serializers: Validate thoroughly, use nested serializers wisely
  3. ViewSets: Use appropriate mixins, implement custom actions
  4. Permissions: Layer permissions appropriately
  5. Testing: High test coverage for business logic
  6. Performance: Query optimization, caching, pagination
  7. Security: HTTPS, CSRF tokens, proper authentication
  8. Documentation: DRF self-documenting + drf-spectacular