Django Complete Guide | MTV, ORM, Admin, REST Framework & Deployment

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

FeatureDjango
Admin panelAuto-generated from models
ORMPython queries → any SQL database
AuthUsers, groups, permissions built in
REST APIVia Django REST Framework
SecurityCSRF, XSS, SQL injection — default
ScaleInstagram (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: