Batteries-Included vs Minimal and Fast
Django is Python's most established web framework: ORM (models as Python classes mapped to database tables), admin panel (auto-generated CRUD UI from models), authentication (user model, sessions, permissions built in), template engine (server-side HTML rendering), form validation, migration system, and test framework. Django's philosophy: everything you need is included. You start building features immediately without choosing libraries for every concern. AI trained on Django has extensive pattern knowledge.
FastAPI is a modern async Python framework focused on API development: Pydantic models for validation and serialization (type-hinted Python classes that validate input automatically), automatic OpenAPI documentation (Swagger UI generated from your type hints), native async/await support (built on Starlette and uvicorn), dependency injection system, and OAuth2/JWT security utilities. FastAPI's philosophy: minimal core, maximum type safety, and let the type system generate documentation and validation for free.
Without framework rules: AI generates Django model patterns in FastAPI (Django ORM does not exist in FastAPI). AI generates Pydantic BaseModel in Django views (Django uses forms and serializers, not Pydantic). AI uses synchronous database calls in FastAPI (FastAPI is async-first, sync calls block the event loop). The frameworks share Python but share almost no conventions. One rule paragraph prevents every cross-framework generation error.
Project Structure: Apps vs Routers
Django project structure: a project contains multiple apps. Each app is a self-contained module: models.py (database models), views.py (request handlers), urls.py (URL routing), admin.py (admin panel registration), forms.py (validation), serializers.py (API serialization with DRF), tests.py, and migrations/. The project settings.py lists installed apps. AI rule: 'Django: organize by apps (users/, orders/, products/). Each app: models.py, views.py, urls.py, admin.py, serializers.py. Register apps in settings.INSTALLED_APPS.'
FastAPI project structure: a main application with routers. Each feature area is a router module: app/routers/users.py (endpoints), app/models/ (SQLAlchemy or Tortoise ORM models), app/schemas/ (Pydantic models for request/response), app/dependencies/ (dependency injection functions), and app/core/ (config, security). The main app includes routers: app.include_router(users_router, prefix='/api/users'). AI rule: 'FastAPI: organize by feature routers. Each feature: router (endpoints), schemas (Pydantic), models (SQLAlchemy). Include routers in main app.'
The structure rule prevents: AI creating a Django apps/ structure in a FastAPI project (apps do not exist in FastAPI), generating admin.py files in FastAPI (no admin panel), or creating views.py in FastAPI (FastAPI uses routers with path operations). The directory structure is the first thing the AI must get right — every file creation follows the project convention.
- Django: apps with models.py, views.py, urls.py, admin.py — registered in settings.py
- FastAPI: routers with schemas/, models/, dependencies/ — included in main app
- Django: one migrations/ directory per app, managed by manage.py migrate
- FastAPI: Alembic for migrations (separate tool), not built into the framework
- AI error: admin.py in FastAPI = useless. routers/ in Django = unconventional
Django: create a users/ app with models.py, views.py, urls.py, admin.py. FastAPI: create routers/users.py with schemas/user.py. The directory structure is the first thing AI generates — wrong structure = every subsequent file in the wrong place.
Database: Django ORM vs SQLAlchemy
Django ORM: models defined as Python classes inheriting from django.db.models.Model. class User(models.Model): email = models.EmailField(unique=True). Queries: User.objects.filter(role='admin').order_by('-created_at'). The ORM is: built into Django (no separate installation), tightly integrated (admin, forms, serializers all use the model), migration-managed (makemigrations + migrate), and synchronous by default (async support is recent and limited).
FastAPI with SQLAlchemy: models defined as Python classes with SQLAlchemy's declarative base. class User(Base): __tablename__ = 'users'; email = Column(String, unique=True). Queries: session.query(User).filter(User.role == 'admin').order_by(User.created_at.desc()). Or async with SQLAlchemy 2.0: async with async_session() as session: result = await session.execute(select(User)). SQLAlchemy is: a separate library (installed independently), database-agnostic, and supports both sync and async.
The ORM rule: 'Django: use models.Model, QuerySet API (objects.filter, objects.create), makemigrations for schema changes. Never import SQLAlchemy in a Django project.' 'FastAPI: use SQLAlchemy with async session (SQLAlchemy 2.0 style), Alembic for migrations. Pydantic schemas for API input/output, SQLAlchemy models for database. Never import django.db in a FastAPI project.' The ORM choice cascades into query syntax, migration tools, and validation patterns.
Validation: Forms/Serializers vs Pydantic
Django validation: forms (Django Forms) for HTML form validation, serializers (Django REST Framework) for API validation. class UserSerializer(serializers.ModelSerializer): class Meta: model = User; fields = ['email', 'name']. DRF serializers: validate input, serialize output, handle nested relationships, and integrate with the Django ORM. AI rule: 'Django API: use DRF serializers for validation and serialization. ModelSerializer for CRUD. Custom serializers for complex validation. Never Pydantic in Django.'
FastAPI validation: Pydantic BaseModel classes with type hints. class CreateUser(BaseModel): email: EmailStr; name: str = Field(min_length=1, max_length=100). FastAPI validates request bodies against Pydantic models automatically: @app.post('/users') async def create_user(user: CreateUser). Invalid input: FastAPI returns 422 with detailed validation errors. Pydantic models also generate the OpenAPI schema (Swagger docs show the expected format). AI rule: 'FastAPI: Pydantic BaseModel for all request/response schemas. Type hints drive validation and documentation. Never DRF serializers.'
The validation rule prevents the most impactful error: AI generating DRF serializers in FastAPI (DRF is Django-only) or Pydantic models in Django views (Django does not process Pydantic automatically). The validation system is framework-determined: Django = DRF serializers + Django forms. FastAPI = Pydantic models. One rule about the validation system aligns every input/output handling pattern.
- Django: DRF serializers (ModelSerializer, custom) + Django Forms for HTML
- FastAPI: Pydantic BaseModel with type hints — auto-validation, auto-OpenAPI docs
- Django: serializer validates + serializes + handles relationships with ORM integration
- FastAPI: Pydantic validates input, separate SQLAlchemy model for database
- AI error: DRF serializer in FastAPI = import error. Pydantic in Django = not processed
FastAPI Pydantic: class CreateUser(BaseModel): email: EmailStr. One type hint gives you: input validation (400 on invalid email), OpenAPI schema (Swagger shows expected format), and TypeScript client generation. Django DRF: separate serializer class for each purpose. Pydantic does three jobs with one definition.
Async: Sync-Default vs Async-First
Django is sync-default. Traditional Django views are synchronous: def user_list(request): users = User.objects.all(); return JsonResponse(list(users)). Django 4.1+ supports async views (async def) and async ORM queries (limited), but most Django code and third-party packages are synchronous. Running async code in Django requires: async view + async ORM (or sync_to_async wrapper). AI rule: 'Django: use synchronous views and ORM by default. Use async views only when explicitly needed (WebSocket, long-running I/O). Wrap sync ORM calls with sync_to_async if used in async views.'
FastAPI is async-first. Endpoints are async by default: async def user_list(): users = await session.execute(select(User)). FastAPI runs on uvicorn (ASGI server), handles concurrent requests via asyncio, and expects async I/O throughout (async database drivers, async HTTP clients). Synchronous code blocks the event loop: never use time.sleep(), synchronous database calls, or synchronous file I/O in FastAPI handlers. AI rule: 'FastAPI: all handlers are async. Use async database drivers (asyncpg, databases). Never synchronous I/O in handlers (blocks the event loop). Use run_in_executor for CPU-bound work.'
The async rule is critical: AI generating synchronous Django ORM calls in FastAPI blocks the event loop (the entire server handles one request at a time). AI generating async/await in traditional Django views adds complexity for no benefit (Django runs synchronously anyway). The concurrency model is framework-determined: Django = synchronous processes. FastAPI = asynchronous event loop. One rule prevents the most impactful performance error.
Synchronous Django ORM call in a FastAPI async handler: blocks the event loop. The entire server handles one request at a time until the query completes. FastAPI is async-first — all I/O must be async. One sync call collapses concurrency from thousands to one.
Ready-to-Use Rule Templates
Django CLAUDE.md template: '# Framework (Django + DRF). Python web framework with Django REST Framework for APIs. Structure: apps (users/, orders/) with models.py, views.py, urls.py, serializers.py, admin.py. ORM: Django ORM (models.Model, QuerySet API, objects.filter/create/update). Validation: DRF serializers (ModelSerializer for CRUD, custom for complex). Migrations: makemigrations + migrate. Auth: Django built-in (User model, sessions, permissions). Admin: register models in admin.py. Views: synchronous by default. Never use FastAPI/Pydantic/SQLAlchemy patterns.'
FastAPI CLAUDE.md template: '# Framework (FastAPI). Async Python API framework. Structure: routers/ (endpoints), schemas/ (Pydantic), models/ (SQLAlchemy), dependencies/ (DI). Validation: Pydantic BaseModel with type hints — auto-validation + auto-OpenAPI docs. ORM: SQLAlchemy 2.0 async (select, async_session). Migrations: Alembic. Handlers: async def, return Pydantic models. Database: asyncpg driver, async sessions. Docs: auto-generated at /docs (Swagger) and /redoc. Never use Django ORM, DRF serializers, or synchronous I/O in handlers.'
The templates capture the most impactful rules: project structure, ORM choice, validation system, async model, and negative rules. Copy the template for your framework. The negative rules (never FastAPI patterns in Django, never Django patterns in FastAPI) are as important as the positive rules — they create the hard boundary that prevents cross-contamination.
Comparison Summary
Summary of Django vs FastAPI AI rules.
- Structure: Django apps (models/views/urls/admin) vs FastAPI routers (schemas/models/dependencies)
- ORM: Django ORM (built-in, objects.filter) vs SQLAlchemy (separate, async select)
- Validation: DRF serializers vs Pydantic BaseModel with type hints
- Async: Django sync-default vs FastAPI async-first — sync in FastAPI blocks the event loop
- Admin: Django admin auto-generated vs FastAPI has none (build your own or use third-party)
- Docs: Django needs DRF + drf-spectacular vs FastAPI auto-generates OpenAPI from type hints
- Migrations: Django makemigrations vs Alembic (separate tool for FastAPI)
- Templates: one paragraph per framework prevents all cross-contamination errors