Django Complete Guide | MTV, ORM, Admin, REST Framework & Deployment
이 글의 핵심
Django is Python's 'batteries included' web framework — used by Instagram, Pinterest, and Mozilla. This guide covers everything from project setup to production deployment, including the built-in Admin panel and Django REST Framework.
Why Django?
Django’s philosophy is “don’t repeat yourself” and “batteries included”. It ships with everything a web application needs:
- ORM — write Python instead of SQL; automatic migrations
- Admin panel — auto-generated CRUD interface from your models
- Auth system — users, groups, permissions, sessions built in
- Security — CSRF, XSS, SQL injection protection by default
- REST Framework — best-in-class API layer (separate package)
Flask: ~1,000 req/sec (minimal, build everything yourself)
Django: ~800 req/sec (full-stack, batteries included)
FastAPI: ~3,000 req/sec (API-only, async)
Installation & Project Setup
pip install django
django-admin startproject myproject
cd myproject
python manage.py runserver
# → http://localhost:8000
Create an app (Django organizes code into apps):
python manage.py startapp users
Register your app in settings.py:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
# ...
'users', # Your new app
]
Project structure:
myproject/
├── manage.py # Command-line utility
├── myproject/
│ ├── settings.py # Configuration
│ ├── urls.py # Root URL configuration
│ └── wsgi.py # WSGI entry point
└── users/
├── models.py # Database models
├── views.py # Request handlers
├── urls.py # App URL patterns
├── admin.py # Admin registration
└── tests.py # Unit tests
Models — Define Your Database Schema
Django models map directly to database tables. No SQL needed.
# users/models.py
from django.db import models
class User(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
age = models.IntegerField(null=True, blank=True)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at'] # Newest first by default
def __str__(self):
return self.name
Apply the schema to your database:
python manage.py makemigrations # Generate migration files
python manage.py migrate # Apply to the database
ORM Queries
# Create
user = User.objects.create(name="Alice", email="alice@example.com")
# Read
all_users = User.objects.all()
active_users = User.objects.filter(is_active=True)
alice = User.objects.get(email="alice@example.com") # Raises if not found
# Update
User.objects.filter(id=1).update(is_active=False)
# Delete
User.objects.filter(is_active=False).delete()
# Chaining
recent_active = (
User.objects
.filter(is_active=True)
.order_by('-created_at')
[:10] # SQL LIMIT 10
)
Views — Handle HTTP Requests
Function-Based Views (FBV)
# users/views.py
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from .models import User
@require_http_methods(["GET"])
def user_list(request):
users = User.objects.all().values('id', 'name', 'email')
return JsonResponse(list(users), safe=False)
@require_http_methods(["GET"])
def user_detail(request, user_id):
try:
user = User.objects.get(id=user_id)
return JsonResponse({
'id': user.id,
'name': user.name,
'email': user.email,
})
except User.DoesNotExist:
return JsonResponse({'error': 'User not found'}, status=404)
Class-Based Views (CBV)
Django’s generic CBVs reduce boilerplate for common patterns:
from django.views.generic import ListView, DetailView, CreateView
from django.urls import reverse_lazy
class UserListView(ListView):
model = User
template_name = 'users/list.html'
context_object_name = 'users'
paginate_by = 10 # Auto-pagination
class UserDetailView(DetailView):
model = User
template_name = 'users/detail.html'
context_object_name = 'user'
class UserCreateView(CreateView):
model = User
fields = ['name', 'email', 'age']
success_url = reverse_lazy('user_list') # Redirect after creation
URL Configuration
# users/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.user_list, name='user_list'),
path('<int:user_id>/', views.user_detail, name='user_detail'),
]
Include app URLs in the root configuration:
# myproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/users/', include('users.urls')),
]
Django REST Framework (DRF)
DRF is the standard way to build REST APIs with Django. Install it:
pip install djangorestframework
Add to INSTALLED_APPS:
INSTALLED_APPS = [
# ...
'rest_framework',
'users',
]
Serializer — JSON ↔ Python Object Conversion
# users/serializers.py
from rest_framework import serializers
from .models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'name', 'email', 'age', 'is_active', 'created_at']
read_only_fields = ['id', 'created_at']
ViewSet — CRUD in One Class
A ModelViewSet automatically provides list, create, retrieve, update, and destroy endpoints:
# users/views.py
from rest_framework import viewsets, permissions
from .models import User
from .serializers import UserSerializer
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
# Optional: filter by query param (?active=true)
qs = super().get_queryset()
if self.request.query_params.get('active'):
qs = qs.filter(is_active=True)
return qs
Router — Auto-Generate URLs
# users/urls.py
from rest_framework.routers import DefaultRouter
from .views import UserViewSet
router = DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = router.urls
# Generates:
# GET/POST /users/
# GET/PUT/PATCH/DELETE /users/{id}/
Admin Panel
Django’s admin panel is auto-generated from your models. Customize it:
# users/admin.py
from django.contrib import admin
from .models import User
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
list_display = ['id', 'name', 'email', 'is_active', 'created_at']
list_filter = ['is_active', 'created_at']
search_fields = ['name', 'email']
ordering = ['-created_at']
list_per_page = 25
readonly_fields = ['created_at', 'updated_at']
Create a superuser and log in:
python manage.py createsuperuser
# → http://localhost:8000/admin
The admin panel gives your team a full CRUD interface with search, filtering, and pagination — zero frontend code required.
Authentication
Built-in Session Auth
from django.contrib.auth.decorators import login_required
@login_required # Redirects to /login/ if not authenticated
def protected_view(request):
return JsonResponse({'user': request.user.username})
JWT Authentication (for APIs)
pip install djangorestframework-simplejwt
Configure DRF to use JWT:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
Add token endpoints:
# urls.py
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
urlpatterns = [
path('api/token/', TokenObtainPairView.as_view()), # POST → access + refresh tokens
path('api/token/refresh/', TokenRefreshView.as_view()), # POST → new access token
]
Usage:
# Get tokens
curl -X POST http://localhost:8000/api/token/ \
-d '{"username": "alice", "password": "secret"}'
# → {"access": "eyJ...", "refresh": "eyJ..."}
# Use the access token
curl http://localhost:8000/api/users/ \
-H "Authorization: Bearer eyJ..."
Deployment
Gunicorn (Production WSGI Server)
pip install gunicorn
gunicorn myproject.wsgi:application --bind 0.0.0.0:8000 --workers 4
Docker
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Collect static files
RUN python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "4"]
Production Settings Checklist
# settings_production.py
DEBUG = False
ALLOWED_HOSTS = ['yourdomain.com']
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ['DB_NAME'],
'USER': os.environ['DB_USER'],
'PASSWORD': os.environ['DB_PASSWORD'],
'HOST': os.environ['DB_HOST'],
}
}
STATIC_ROOT = '/var/www/static/'
SECRET_KEY = os.environ['SECRET_KEY']
Summary
| Feature | Django |
|---|---|
| Admin panel | Auto-generated from models |
| ORM | Python queries → any SQL database |
| Auth | Users, groups, permissions built in |
| REST API | Via Django REST Framework |
| Security | CSRF, XSS, SQL injection — default |
| Scale | Instagram (1B+ users) proves it |
Django’s biggest win is speed of development — the Admin panel alone eliminates weeks of backoffice UI work. Use it when you need a complete web application, not just an API.
Related posts: