본문 바로가기
Programming/Django

[Python Django] The Practical Guide - Data & Models (3)

by Mandy's 2025. 7. 22.
Model URLs
 <li><a href="{% url "book-detail" book.id %}">{{ book.title }} (Rating: {{ book.rating }})</a> </li>
 

index.html

urlpatterns = [
path("", views.index),
path("<int:id>", views.book_detail, name="book-detail")
]

urls.py

  • URL을 생성하기 위해 {% url %} 템플릿 태그 사용

def get_absolute_url(self):
return reverse("book-detail", args=[self.id])
 

models.py

<li><a href="{{ book.get_absolute_url }}">{{ book.title }} (Rating: {{ book.rating }})</a> </li>
 

index.html

 

  • 모델에서 정의한 get_absolute_url()을 그대로 호출해 링크를 생성
  • 가독성 향상 + DRY 원칙(중복 피하기)
간단한 경우: {% url %} 사용
반복적으로 URL을 생성하거나 많은 템플릿에서 사용되는 경우: get_absolute_url() 정의해서 사용하면 유지보수에 훨씬 좋음!

 

models.py

Slugfield
slug = models.SlugField(default="", null=False) # harry-potter-1
 
 
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super().save(*args, **kwargs)
 
# models.py

 

  • .save() 호출 시 자동으로 slug 생성 (또는 갱신)
  • slugify("Harry Potter 1") → "harry-potter-1"처럼 영어 소문자와 하이픈 형태로 변환

 

python manage.py makemigrations
Migrations for 'book_outlet':
  book_outlet/migrations/0003_book_slug.py
    + Add field slug to book​
slugfield란?

 

  • models.py에서 다음을 수정하거나 추가했을 때:
    • 필드 추가/수정/삭제
    • 모델 추가/삭제
    • 필드 옵션 변경 (예: blank=True, null=False, default='x' 등)

python manage.py migrate

  • makemigrations를 한 후, 변경 내용을 DB에 적용하고 싶을 때
 python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, book_outlet, contenttypes, sessions
Running migrations:
  Applying book_outlet.0003_book_slug... OK
❯ python manage.py shell
7 objects imported automatically (use -v 2 for details).

Python 3.13.5 experimental free-threading build (main, Jul 15 2025, 14:26:58) [Clang 15.0.0 (clang-1500.3.9.4)]
Type 'copyright', 'credits' or 'license' for more information
IPython 9.4.0 -- An enhanced Interactive Python. Type '?' for help.
Tip: IPython 9.0+ has hooks to integrate AI/LLM completions.

In [1]: from book_outlet.models import Book

In [2]: Book.objects.get(title="Harry Potter 1").save()

In [3]: Book.objects.get(title="Harry Potter 1").slug
Out[3]: 'harry-potter-1'

In [4]: Book.objects.get(title="Lord of the Rings").save()

In [5]: Book.objects.get(title="Lord of the Rings").slug
Out[5]: 'lord-of-the-rings'

In [6]: Book.objects.get(title="My Story").save()

In [7]: Book.objects.get(title="My Story").slug
Out[7]: 'my-story'

In [8]: 

In [8]: Book.objects.get(title="Some random book").save()

In [9]: Book.objects.get(title="Some random book").slug
Out[9]: 'some-random-book'

 

 


from django.shortcuts import get_object_or_404, render
from django.http import Http404

from .models import Book

# Create your views here.

def index(request):
books = Book.objects.all()
return render(request, "book_outlet/index.html", {
"books": books
})

def book_detail(request, slug):
# try:
# book = Book.objects.get(pk=id)
# except:
# raise Http404()
book = get_object_or_404(Book, slug=slug) # 위와 같은 방법.. 404 page 생성
return render(request, "book_outlet/book_detail.html", {
"title": book.title,
"author": book.author,
"rating": book.rating,
"is_bestseller": book.is_bestselling
})

views.py

def get_absolute_url(self):
return reverse("book-detail", args=[self.slug])
 

models.py

  • reverse("book-detail", args=[self.slug]): urls.py에서 name이 "book-detail"인 URL 패턴을 찾아서, args에 들어있는 값을 URL의 변수 자리에 넣어줍니다.

# book_outlet/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path("", views.index),
    path("<slug:slug>/", views.book_detail, name="book-detail"),  # 수정됨
]
오류

urls.py는 <int:id>가 아니라 <slug:slug>로 받아야 self.slug와 매칭됨. 현재는 args=[self.slug]인데 URL 패턴에서는 id만 찾고 있어서 NoReverseMatch가 발생한 것. 수정 후 서버 재시작하거나 저장만 해도 반영됩니다.

slug = models.SlugField(default="", null=False, db_index=True, primary_key=True) # Harry Potter 1 => harry-potter-1
 

models.py

  • db_index = True, primary_key=True

 


Aggregation & Ordering
def index(request):
books = Book.objects.all()
num_books = books.count()
avg_rating = books.aggregate(Avg("rating"))
 
return render(request, "book_outlet/index.html", {
"books": books,
"total_number_of_books": num_books,
"average_rating": avg_rating
})

views.py

<hr>
<p>Total Number of Books: {{ total_number_of_books }}</p>
<p>Average Rating: {{ average_rating }}</p>
</hr>

index.html

<p>Average Rating: {{ average_rating.rating__avg }}</p>
 

index.html

books = Book.objects.all().order_by("-title")

views.py

  • title말고 rating 등 다른 것도 가능