Skip to content
Back to skills

Django Patterns

ASecurity

Django architecture patterns including DRF, ORM optimization, signals, middleware, and project structure

  • 4 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 10, 2026
developmentpythongosqldjango

Security analysis

A100/100

Scanned September 10, 2026

npx -y skills add micsapp/micstec-skills --skill django-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Django Patterns?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Django Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/micsapp-django-patterns/badge)](https://www.skillsdirectory.com/skills/micsapp-django-patterns)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: django-patterns
description: Django architecture patterns including DRF, ORM optimization, signals, middleware, and project structure
---

# Django Patterns

## Project Structure

Organize Django projects with a clear separation between apps, shared utilities, and configuration.

```
project/
  config/
    settings/
      base.py
      local.py
      production.py
    urls.py
    wsgi.py
  apps/
    users/
      models.py
      serializers.py
      views.py
      services.py
      selectors.py
      urls.py
      tests/
    orders/
      ...
  common/
    models.py
    permissions.py
    pagination.py
```

Keep business logic in `services.py` (write operations) and `selectors.py` (read operations). Views should remain thin.

## ORM Optimization

```python
# select_related for ForeignKey / OneToOne (SQL JOIN)
orders = Order.objects.select_related("customer", "customer__profile").all()

# prefetch_related for ManyToMany / reverse FK (separate query)
authors = Author.objects.prefetch_related(
    Prefetch("books", queryset=Book.objects.filter(published=True))
).all()

# Defer fields you don't need
posts = Post.objects.defer("body", "metadata").filter(status="published")

# Use .only() when you need just a few columns
emails = User.objects.only("id", "email").filter(is_active=True)

# Bulk operations
Product.objects.bulk_create(products, batch_size=1000)
Product.objects.bulk_update(products, ["price", "stock"], batch_size=1000)
```

Always check queries with `django-debug-toolbar` or `connection.queries` in tests.

## Django REST Framework Serializers

```python
class OrderSerializer(serializers.ModelSerializer):
    customer_name = serializers.CharField(source="customer.full_name", read_only=True)
    items = OrderItemSerializer(many=True, read_only=True)
    total = serializers.SerializerMethodField()

    class Meta:
        model = Order
        fields = ["id", "customer_name", "items", "total", "created_at"]
        read_only_fields = ["id", "created_at"]

    def get_total(self, obj):
        return sum(item.price * item.quantity for item in obj.items.all())

    def validate(self, data):
        if data.get("start_date") and data.get("end_date"):
            if data["start_date"] >= data["end_date"]:
                raise serializers.ValidationError("end_date must be after start_date")
        return data
```

## Signals

```python
from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=Order)
def order_created_handler(sender, instance, created, **kwargs):
    if created:
        send_order_confirmation.delay(instance.id)
        update_inventory.delay(instance.id)
```

Prefer signals for cross-app side effects. For same-app logic, call services directly.

## Custom Middleware

```python
import time
import logging

logger = logging.getLogger(__name__)

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

    def __call__(self, request):
        start = time.monotonic()
        response = self.get_response(request)
        duration = time.monotonic() - start
        logger.info(f"{request.method} {request.path} {response.status_code} {duration:.3f}s")
        return response
```

## Anti-Patterns

- Putting business logic in views or serializers instead of service layers
- Using `Model.objects.all()` without pagination in list endpoints
- N+1 queries from missing `select_related` / `prefetch_related`
- Overusing signals for same-app logic (makes flow hard to trace)
- Storing secrets in `settings.py` instead of environment variables
- Running raw SQL without parameterized queries

## Checklist

- [ ] Business logic lives in services/selectors, not views
- [ ] All list queries use `select_related` or `prefetch_related` where needed
- [ ] Serializers validate input data with custom `validate` methods
- [ ] Settings split into base/local/production modules
- [ ] Migrations are reviewed before merging
- [ ] Bulk operations used for batch inserts/updates
- [ ] Custom middleware follows the WSGI callable pattern
- [ ] Tests cover model constraints, serializer validation, and view permissions

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…