출시·고도화 중
Django 안내서 · 4/6
Django 애플리케이션의 데이터는 ORM 모델로 정의하고, 마이그레이션으로 데이터베이스에 반영하며, QuerySet으로 읽고 씁니다. 사용자 입력은 폼으로 검증하고, 요청 사이에 이어지는 상태는 세션과 인증 시스템이 관리합니다.
모델 클래스의 속성 하나가 테이블의 열 하나입니다. ForeignKey는 다대일, ManyToManyField는 다대다, OneToOneField는 일대일 관계를 만듭니다.
# polls/models.py
from django.db import models
from django.utils import timezone
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField(default=timezone.now)
class Meta:
ordering = ["-pub_date"]
def __str__(self):
return self.question_text
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name="choices")
choice_text = models.CharField(max_length=200)
votes = models.PositiveIntegerField(default=0)on_delete는 참조하는 객체가 지워질 때의 동작입니다. CASCADE는 함께 지우고, PROTECT는 삭제를 막으며, SET_NULL은 값을 비웁니다(null=True 필요). related_name을 주면 question.choices.all()처럼 반대 방향에서 접근할 이름이 정해집니다.
모델을 바꾼 뒤에는 두 단계로 데이터베이스에 반영합니다.
# 모델 변경을 감지해 migrations/ 에 파일 생성
python manage.py makemigrations polls
# 실제로 실행될 SQL 확인
python manage.py sqlmigrate polls 0001
# 데이터베이스에 적용
python manage.py migrate
# 적용 상태 보기
python manage.py showmigrations마이그레이션 파일은 코드와 함께 버전 관리에 넣습니다. 팀원과 운영 서버는 같은 파일로 migrate만 실행하면 같은 스키마를 얻습니다. 기본 데이터베이스는 SQLite지만 운영에서는 PostgreSQL을 많이 쓰며, DATABASES 설정만 바꾸면 됩니다.
Model.objects는 매니저이고, 여기서 얻는 QuerySet은 지연 평가됩니다. 필터를 이어 붙이는 동안에는 쿼리가 실행되지 않고, 반복하거나 목록으로 바꾸는 순간 한 번 실행됩니다.
from datetime import timedelta
from django.db.models import Count, F, Q
from django.utils import timezone
from polls.models import Choice, Question
# 생성과 저장
q = Question.objects.create(question_text="좋아하는 언어는?")
q.choices.create(choice_text="Python")
# 필터: 필드__조회 형식
recent = Question.objects.filter(pub_date__gte=timezone.now() - timedelta(days=7))
python_q = Question.objects.filter(question_text__icontains="python")
# 관계를 따라 필터하고, 개수로 주석 달기
popular = (
Question.objects.annotate(total=Count("choices"))
.filter(total__gt=1)
.order_by("-total")
)
# OR 조건과 원자적 증가
Question.objects.filter(Q(question_text__startswith="왜") | Q(question_text__startswith="어떻게"))
Choice.objects.filter(pk=1).update(votes=F("votes") + 1)
# N+1 쿼리 피하기
for choice in Choice.objects.select_related("question"):
print(choice.question.question_text)select_related는 외래 키를 JOIN으로 함께 가져오고, prefetch_related는 다대다·역방향 관계를 별도 쿼리 한 번으로 미리 가져옵니다. python manage.py shell에서 직접 실험해 보면 익히기 쉽습니다. 여러 작업을 한꺼번에 성공 또는 실패시키려면 transaction.atomic()으로 감쌉니다.
폼은 입력을 받아 검증하고 정리된 값(cleaned_data)을 돌려줍니다. 모델과 필드가 같다면 ModelForm이 편합니다.
# polls/forms.py
from django import forms
from .models import Question
class QuestionForm(forms.ModelForm):
class Meta:
model = Question
fields = ["question_text"]
def clean_question_text(self):
text = self.cleaned_data["question_text"].strip()
if not text.endswith("?"):
raise forms.ValidationError("질문은 물음표로 끝나야 합니다.")
return text# polls/views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render
from .forms import QuestionForm
@login_required
def create(request):
if request.method == "POST":
form = QuestionForm(request.POST)
if form.is_valid():
question = form.save()
return redirect("polls:detail", question_id=question.pk)
else:
form = QuestionForm()
return render(request, "polls/create.html", {"form": form})템플릿의 POST 폼에는 반드시 {% csrf_token %}을 넣어야 CSRF 미들웨어를 통과합니다. 저장에 성공하면 리다이렉트해서 새로 고침으로 같은 요청이 다시 전송되지 않게 합니다.
세션은 사용자별 상태를 서버 쪽에 저장하고, 브라우저에는 세션 ID 쿠키만 둡니다. 뷰에서는 request.session을 사전처럼 씁니다.
def add_to_cart(request, item_id):
cart = request.session.get("cart", [])
cart.append(item_id)
request.session["cart"] = cart
return redirect("shop:cart")인증 시스템(django.contrib.auth)은 사용자·그룹·권한 모델과 로그인·로그아웃·비밀번호 변경 뷰를 제공합니다. path("accounts/", include("django.contrib.auth.urls"))를 추가하면 기본 인증 URL이 생기고, 뷰에서는 request.user로 현재 사용자를, @login_required나 LoginRequiredMixin으로 접근 제한을 겁니다. 새 프로젝트라면 처음부터 AbstractUser를 상속한 사용자 모델을 만들고 AUTH_USER_MODEL로 지정해 두는 것이 공식 권장 사항입니다. 나중에 바꾸기가 매우 어렵기 때문입니다.
ForeignKey 등은 관계를 나타냅니다.makemigrations로 변경을 기록하고 migrate로 적용합니다.select_related·prefetch_related로 쿼리 수를 줄입니다.django.contrib.auth로 다룹니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.