[Do it 장고 + 부트스트랩] 8장. 웹 페이지 만들기

정재욱·2023년 6월 9일
0
post-thumbnail

1. URL 설정하기

웹 사이트 방문자가 여러 정보에 접근할 수 있게 하려면 첫 페이지 뿐만 아니라 모든 페이지마다 각각 URL을 추가로 지정해야 한다.

표지판 역할을 하는 urls.py

장고 프로젝트 폴더(do_it_django_prj)에서 urls.py를 열어보자.
이 파일은 사용자가 장고로 개발한 웹 사이트에 방문했을 때 어떤 페이지로 들어가야 하는지를 알려준다.

우리가 관리자 페이지로 넘어갈 수 있었던 것도, 방문자가 '서버 IP/admin/'으로 접속하면 admin.site.urls에 정의된 내용을 찾아 처리하라고 정해 놓았기 때문이다.

장고 프로젝트를 만들면 다음과 같이 urls.py가 자동으로 생성.

from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

필요한 페이지 살펴보기

해당 책에서는 대문 페이지, 블로그 페이지, 자기소개 페이지를 구성할 것이다.

그렇다면 각 페이지마다 필요한 하위 페이지는 어떤 것이 있을까?

  • 대문 페이지

    • url : '도메인'
    • 방문자를 맞이하는 페이지 이므로 따로 하위 페이지가 필요하지 않다.
  • 블로그 페이지

    • 포스트 목록 페이지

      • '도메인/blog/'
      • 블로그의 여러 포스트를 목록 형태로 보여주는 페이지.
    • 포스트 상세 페이지

      • '도메인/blog/포스트의 pk'
      • 포스트를 클릭하면 그 포스트의 전문과 댓글을 보여주는 페이지
  • 자기소개 페이지
    • '도메인/about_me/' 일때 나타나는 페이지.

포스트 목록 페이지 만들기

블로그 페이지 URL로 접속하기

python manage.py runserver로 서버를 실행시킨 후, '127.0.0.1:8000/blog/'에 들어가 보면 아마 오류가 발생할 것이다.

왜냐하면 urls.py에 아직 admin/에 해당하는 내용밖에 없기 때문이다.

blog/urls.py 만들기

이제 urls.py에 'blog/'로 접속하는 경우를 추가해보자.

방문자가 'blog/'로 접속할 때blog 앱 폴더의 urls.py를 참고하도록 설정해보자.

  1. do_it_django_prj/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
	path('blog/', include('blog.urls'),  # 새로 추가!
    path('admin/', admin.site.urls),
]
  1. blog앱 폴더에 urls.py를 다음과 같이 생성하기
from django.urls import path
from . import views

urlpatterns = [
    # 해당 부분을 채워 나갈 것이다!!!
]

이렇게 blog앱 폴더에 따로 urls.py를 만드는 이유는 앱마다의 독립성을 챙기기 위함이다.

즉, 만약 다른 프로젝트를 진행할 때 저번에 만들었던 blog 앱이 정말 마음에 들었다면, blog앱만 복사해가서 활용할 수 있을것이다.
왜냐하면 blog/urls.py에 해당 blog 앱을 사용하기 위한 url이 이미 정의되어 있기 때문!!

path()와 include()의 차이?

  • path('path/', app.views.funct) : http://127.0.0.1:8000/path/로 접속했을 때 app.views.funct 로부터 반환받은 페이지를 url에 등록해주는 함수
  • path('products/', include('product.urls')) : http://127.0.0.1:8000/products/ 로 시작하는 모든 URL 들은 product 폴더안의 urls.py 파일 안에서 관리할 것이라는 의미

우리는 include()를 사용했기 때문에 blog앱 폴더에 따로 urls.py를 사용할 수 있었다.

URL을 등록할 때 사용되는 함수는 path()include()가 있는데 결론부터 말씀드리자면 path() 대신 include()를 사용하는 이유는 우리의 프로젝트를 좀 더 효율적으로 구현하기 위해서다!

url을 연결할 때 urlpatterns에 path들을 단순 나열해두면 path가 많아졌을 때 관리가 어렵다.
그래서 django.urls의 include를 사용해 url들을 계층적으로 관리한다.

먼저 url을 관리할 앱을 myproject/setting.py의 INSTALLED_APPS에 등록한다. (7장에서 실습함!)

각 앱에 추가로 urls.py 생성한다. ( ex. app_one/urls.py, app_two/urls.py )

원래의 myproject/urls.py에 와서path('app_ones/', include('app_one.urls'))코드를 통해 외부 url을 등록한다.

path('app_ones/', include('app_one.urls'))의 의미는 'http://127.0.0.1:8000/app_ones/1', 'http://127.0.0.1:8000/app_ones/2', 'http://127.0.0.1:8000/app_ones/ab' 등등 app_ones/로 시작하는 모든 url을 app_one.urls에서 관리하겠다는 의미다.

다음으로 app_one/urls.py에 url을 동록한다.

즉, 하나의 프로젝트에 app이 여러 개 존재한다면, 각각의 앱 안에 urls.py 파일을 만들고 프로젝트의 urls.py에서 app의 urls.py를 연결해 URL관리를 위탁할 수 있다.


2. FBV로 페이지 만들기

장고는 MTV 패턴에 따라 urls.py에 들어갈 함수나 클래스 등은 views.py에서 정의한다.
이때 FBVCBV라는 2가지 선택지가 있다.

  • FBV : Function based view
    • 함수에 기반을 둔 방법
    • 함수를 직접 만들어서 원하는 기능을 직접 구현할 수 있음
  • CBV : Class based view
    • 장고가 제공하는 클래스를 활용해 구현하는 방법
    • 장고는 반복적으로 많이 구현하는 것들을 클래스로 미리 만들어서 제공

FBV로 포스트 목록 페이지 만들기

views.py에 index라는 함수를 만들어서 FBV로 구현해보자.

blog/urls.py 내용 추가하기

from django.urls import path
from . import views  # 현재 폴더에 있는 views.py를 사용하겠다는 의미

urlpatterns = [
    path('', views.index),
]

path('', views.index)는 입력된 URL이 'blog/'로 끝난다면, 임포트한 views.py에 정의되어 있는 index() 함수를 실행한다는 의미다.

사용자가 '도메인/blog'에 접속한다면, do_it_django_prj/urls.py의 urlpatterns에 path('blog/', include('blog.urls')를 추가했으므로, 장고는 'blog/urls.py'에 도달하여 해당 urlpatterns를 찾는다.

그리고 path의 첫 인자가 ' '이므로, 최종적으로 '도메인/blog/'로 끝난다면 views.index()를 실행하라는 것이다.

blog/views.py에 index() 함수 정의하기

blog/views.py에 다음과 같이 작성.

from django.shortcuts import render

# FBV 방법
def index(request):
    posts = Post.objects.all().order_by('-pk')

    return render(
        request,
       'blog/index.html',
       )

새로 만든 index() 함수는 단순하다.

장고가 기본으로 제공하는 render() 함수를 사용하여 템플릿 폴더에서 blog 폴더의 index.html 파일을 찾아 방문자에게 보내준다.

템플릿 파일 만들기

blog/templates/blog 폴더를 새로 만든 다음 그 안에 index.html을 만들자.

즉, blog/templates/blog/index.html을 만들고 다음과 같이 작성하자.

<!DOCTYPE html>
<html lang="ko">  # 이 문서는 한국어로 된 문서다 라는 의미
<head>
    <meta charset="UTF-8">
    <title>Blog</title>
</head>
<body>
    <h1>Blog</h1>
</body>
</html>

이후 127.0.0.1:8000/blog/에 접속해보면 화면에 Blog 글자가 나타난다.

블로그 페이지에 포스트 목록 나열하기

이제 포스트 목록을 나열해보자.

먼저 blog/views.py 를 다음과 같이 작성하자.

from django.shortcuts import render
from .models import Post


# Create your views here.
def index(request):
    posts = Post.objects.all().order_by('-pk') #order_by로 pk값의 역순 정렬. 즉, 최신 포스트부터 보여주기

    return render(
        request,
        'blog/index.html',
        {
            'abc': posts,
        }
    )

models.py에 정의되어 있는 Post 모델을 임포트 한다. 그리고 index() 함수에서 Post.objects.all()로 모든 Post 레코드를 가져와 abc라는 키에 저장하자. 마지막으로 render() 함수 안에 posts를 딕셔너리 형태로 추가한다.

포인트는 Post.objects.all()처럼 views.py에서 데이터베이스에 쿼리를 날려 원하는 레코드를 가져올 수 있다는 것!! order_by()도 쿼리의 한 종류다!!

쿼리 -> 데이터베이스의 데이터를 가져오거나 수정, 삭제하는 등의 행위를 하기 위한 요청... 장고 쿼리 명령어로 검색해보면 많은 내용이 있다.

자, 이제 템플릿 파일인 index.html을 장고에서 지원하는 템플릿 언어를 사용하여 수정하자.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Blog</title>
</head>
<body>
    <h1>Blog</h1>

{% for p in abc%}
    <hr/>
    <h2>{{ p.title }}</h2>
    <h4>{{ p.created_at }}</h4>
    <p>{{ p.content }}</p>
{% endfor %}
</body>
</html>

views.py의 index() 함수에서 쿼리를 통해 받아온 post 레코드들을 for문을 이용해 뿌려준다. 이는 템플릿 언어로 {% for p in abc%}로 나타낼 수 있다. index() 함수의 render()안에 딕셔너리 형태로 포스트 레코드들을 abc라는 키에 저장했기 때문에 for문에 abc라고 작성!

이후 for문을 통해 인스턴스화된 Post 모델의 필드는 닷(.) 기호로 접근할 수 있다!!!

다시 웹 브라우저를 새고로침 해보면 블로그 포스트 목록들이 최신 순으로 잘 정렬된 것을 볼 수 있다!!

템플릿 언어?

HTML은 기본적으로 정적인 언어이다. 작성해놓고 나면, 동적으로 무언가를 변경하거나 수정할 수 없다.

그런 HTML 의 한계를 극복하기 위해 사용하는 것이 django 의 템플릿 언어다.

index.html에서 사용한 {% %} , {{ }} 와 같은 구문을 통해 HTML 내에 동적인 구문을 삽입할 수 있게 하는거다.

간단히 말해서, {{ }} 와 같은 구문은 단순 변수 출력을 위해서 쓰는 태그이고,
{% %} 와 같은 구문은 if, for 등 임의의 로직을 실행하기 위해 쓰이는 태그다.

어떤 조건에 따라 HTML 태그를 출력하기도 안하게도 할 수 있고, FOR 문을 통해 특정 HTML 태그들을 반복출력 할 수도 있게 하는, HTML을 보충해주는 역할을 가진 기능이라고 보면 된다.

자세한 내용은 아래 링크를 확인!!!
공식 문서 : https://docs.djangoproject.com/en/3.2/topics/templates/#the-django-template-language
참고 블로그 : https://velog.io/@hidaehyunlee/Django-%ED%85%9C%ED%94%8C%EB%A6%BF-%EC%96%B8%EC%96%B4

MTV 패턴으로서 다시 한 번 이해하기


장고의 중요한 패턴인 MTV 패턴을 보면서 이번에 한 것 들을 이해해보자.

우선 2번에 해당하는 urls.py를 만들었다. 프로젝트폴더의 urls.py에 path('blog/', include('blog.urls')를 작성하여, 'blog/'에 접근하면 'blog/urls.py'에 가라는 의미다.

또한 blog/urls.py에는 urlpatterns = [path('', views.index),]를 작성하여, '도메인/blogs/'에 접근하면 views.py의 index()라는 함수와 연결되게 하였다. 이것이 2->3으로 가는 흐름이다!

그리고 index()함수에서 쿼리를 사용하여 데이터베이스에서 Post 모델의 객체들을 받아왔기 때문에 models.py와 데이터베이스를 연결했다 할 수 있다. 해당 사항은 3 -> 4 -> 5로 가는 흐름이다.

그리고 마지막으로 index() 함수의 리턴 값으로 index.html이 있었다. 이처럼 준비한 자료들을 템플릿에 채워 출력해본 것이다.

FBV로 포스트 상세 페이지 만들기

포스트 목록 페이지를 만들어 봤으니, 포스트를 클릭하면 포스트의 상세 정보를 나타내는 '포스트 상세 페이지'를 만들어보자.

포스트 상세 페이지 URL 정의하기

먼저 blog/urls.py에 다음과 같이 한 줄 추가하자.

from django.urls import path
from . import views

urlpatterns = [
    path('<int:pk>/', views.single_post_page), # 추가
    path('', views.index),
]

새로 추가한 코드는 "만약 /blog/ 뒤에 정수(int) 형태의 값이 붙는 URL 이라면, blog/views.py의 single_post_page()라는 함수에 정의된 대로 처리하라"는 의미이다.

여기서 <int:pk>는 정수 형태의 값을 pk라는 변수에 담아 single_post_page() 함수로 넘기겠다는 의미다.

예를 들어 'blog/1/'은 식별자(pk) 1을 가진 게시물을 나타내고, 이 URL 패턴은 views.py의 single_post_page() 함수를 호출하면서 식별자(pk)값을 전달한다.

장고(Django)에서 <int:pk>는 URL 패턴을 정의하는 데 사용되는 일반적인 표현이다
이러한 방식으로 <int:pk>를 사용하여 URL 패턴을 정의하고, 해당 값을 view.py의 함수에 전달하여 동적인 웹 페이지를 구성할 수 있다.

single_post_page() 함수 정의하기

다음과 같이 blog/views.py에 single_post_page() 함수를 정의하자.

from django.shortcuts import render
from django.views.generic import ListView, DetailView
from .models import Post


# FBV 방법
def index(request):
    posts = Post.objects.all().order_by('-pk')

    return render(
        request,
        'blog/post_list.html',
        {
            'posts': posts,
        }
    )


def single_post_page(request, pk):
    post = Post.objects.get(pk=pk)

    return render(
        request,
        'blog/post_detail.html',
        {
            'post': post,
        }
    )

index() 함수와는 다르게 single_post_page() 함수는 pk를 매개변수로 더 받는다.

Post.objects.get() 명령은 괄호 안의 조건을 만족하는 Post 레코드를 가져오라는 의미다.
여기서는 Post 모델의 pk 필드 값이 single_post_page() 함수의 매개변수로 받은 pk와 같은 레코드를 가져오라는 뜻!

그 다음 가져온 Post 레코드 하나를 blog/single_post_page.html에 담아 렌더링한다.

템플릿 만들기

blog/templates/blog 폴더에 single_post_page.html을 만들자.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>{{ post.title }} - Blog</title>
</head>
<body>
<nav>
    <a href="/blog/">Blog</a>
</nav>
<h1>{{ post.title }}</h1>
<h4>{{ post.created_at }}</h4>
<p>{{ post.content }}</p>
<hr/>
<h3>여기 댓글이 들어올 수 있겠죠?</h3>
</body>
</html>

포스트 제목에 링크 만들기

포스트 목록 페이지에서 포스트의 제목을 클릭하면 해당 포스트의 상세 페이지로 갈 수 있는 기능을 구현해보자.

index.html 수정하기

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Blog</title>
</head>
<body>
<h1>Blog</h1>
{% for p in posts %}
    <hr/>
    <!-- 아래 한 줄 수정 -->
    <h2><a href="{{ p.get_absolute_url }}">{{ p.title }}</a></h2>
    <!-- -->
    <h4>{{ p.created_at }}</h4>
    <p>{{ p.content }}</p>
{% endfor %}

get_absolute_url() 함수 정의하기

blog/models.py 파일에 다음과 같이 추가하자.

from django.db import models
class Post(models.Model):
    title = models.CharField(max_length=30)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    # author: 추후 작성 예정

    def __str__(self):
        return f'[{self.pk}]{self.title}'
	
    # 추가
    def get_absolute_url(self):
        return f'/blog/{self.pk}/'

get_absolute_url()은 Django 모델 클래스의 메서드로, 객체의 절대 URL을 반환하는 데 사용된다.
이 메서드를 정의하면 객체를 대표하는 URL을 간편하게 얻을 수 있다.

index.html을 봐보자.{% for p in posts %}을 통해 Post들을 가져오고 p로 인스턴스화 했기 때문에 p.get_absolute_url()을 사용할 수 있게된다.

우리는 앞서 포스트 상세 페이지를 만들 때 URL이 도메인 뒤에 /blog/레코드의 pk/를 쓰도록 했기 때문에 return 값으로 f'/blog/{self.pk}/'를 사용한다.

대문 페이지와 자기소개 페이지 만들기

single_pages 앱을 통해 대문 페이지와 자기소개 페이지를 만들어보자.

해당 과정을 따라하면서 한번 더 장고의 MTV 패턴을 기억하면서 따라하자!!

single_pages 앱을 위한 URL 지정

대문페이지는 도메인 뒤에 아무 것도 붙이지 않았을 때 나타나는 페이지 이다. 해당 경우는 single_pages 앱에서 처리하도록 장고 프로젝트 폴더의 urls.py를 다음과 같이 수정하자.

# do_it_django_prj/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('blog/', include('blog.urls')),
    path('admin/', admin.site.urls),
    path('', include('single_pages.urls')), # 추가
]

대문 페이지와 자기소개 페이지의 URL 지정하기

single_pages 앱 폴더에 urls.py를 만들고 2가지 URL 패턴에 대한 명령을 추가하자.

  1. 도메인 뒤에 아무 것도 없을 때는 views.py에 있는 landing() 함수를 실행하여 대문 페이지를 보여준다.
  2. 도메인 뒤에 'about_me/'가 붙어 있을 때는 about_me() 함수를 실행하여 자기소개 페이지를 보여준다.
# single_pages/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('about_me/', views.about_me),
    path('', views.landing),
]

views.py에 함수 정의하기

single_pages 앱의 views.py에 landing() 함수와 about_me() 함수를 만들어 FBV 스타일로 페이지를 보여줘야 한다.

landing() 함수에서는 landing.html을 보여주고, about_me() 함수에서는 about_me.html을 보여주도록 설정하자.

# single_pages/view.py
from django.shortcuts import render


def landing(request):
    return render(
        request,
        'single_pages/landing.html'
    )


def about_me(request):
    return render(
        request,
        'single_pages/about_me.html'
    )

데이터베이스와 연결할 필요 없이 단순히 html만 연결해 줄것이므로, blog 앱과 달리 render() 함수 내에 딕셔너리로 인자를 전달할 필요는 없다.

템플릿 파일 만들기

<!-- landing.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>달타냥입니다!</title>
</head>
<body>
<nav>
    <a href="/blog/">Blog</a>
    <a href="/about_me/">About me</a>
</nav>

<h1>안녕하세요. 달타냥입니다.</h1>
<h2>대문페이지</h2>
<h3>아직 만들지 않음</h3>

</body>
</html>
<!-- about_me.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>개발자 달타냥입니다.</title>
</head>
<body>

<nav>
    <a href="/blog/">Blog</a>
    <a href="/about_me/">About me</a>
</nav>

<h1>안녕하세요. 달타냥입니다</h1>
<h2>이력</h2>
<h2>Portfolio</h2>
<h3>아직 공사중입니다. </h3>

</body>
</html>

3. CBV로 페이지 만들기

CBV를 이용하면 FVB보다 훨씬 더 간단하게 페이지를 만들 수 있는 경우가 많다!!

장고에서는 Base View를 통해 기본적인 클래스 기반 뷰를 지원한다. 그리고 웹 개발시 반복적으로 개발해야할 요소를 줄여주는 Generic display views를 지원한다. 이는 Base View와 기타 다양한 믹스인을 상속받아 구현되었다.

API개발에 유용한 DRF(Django Rest Framework)도 CBV(Class Based View)의 철학을 따르기 때문에 CBV를 이해하는 것이 좋다.

앞서 FBV로 만들었던 포스트 목록 페이지와, 포스트 상세 페이지를 CBV를 활용한 방법으로 수정해보자!!

참고
https://wikidocs.net/9623
Generic 뷰 사용하기

CBV로 포스트 목록 페이지 만들기

ListView로 포스트 목록 페이지 만들기

FBV 스타일의 index() 함수를 대체하는 PostList 클래스를 ListView 클래스를 상속하여 만들자.

# blog/views.py
from django.shortcuts import render
from django.views.generic import ListView  # 추가
from .models import Post


class PostList(ListView):  # 추가
    model = Post 
    ordering = '-pk' # pk 값이 작은 순서대로 정렬

# 삭제하기
# FBV
# def index(request):
#     posts = Post.objects.all().order_by('-pk')
#
#     return render(
#         request,
#         'blog/index.html',
#         {
#             'abc': posts,
#         }
#     )


def single_post_page(request, pk):
    post = Post.objects.get(pk=pk)

아주 간단하게 ListView를 사용할 것이고, model은 Post다 라고 선언한 것이다.

urls.py 수정하기

blog/urls.py를 열어 URL 끝이 /blog/ 일 때는 PostList 클래스로 처리하도록 수정하자.

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

urlpatterns = [
    path('', views.PostList.as_view()), # 추가
    path('<int:pk>/', views.single_post_page),
    # path('', views.index),
]

템플릿 파일 지정하기

장고가 제공하는 ListView는 <app name>/<model name>_list.html 파일을 기본 템플릿으로 사용하도록 설정되어 있다.

즉, 우리는 model=Post 라고 등록했으므로, post_list.html이 필요하다. 만약 뭐, 모델 이름이 ABC라면, abc_list.html이 필요할 것이다.

또는 PostList 클래스에서 template_name을 직접 지정하여 템플릿 이름을 직접 지정할 수 있다. 즉, 이미 만들어 뒀던 index.html을 사용하고 싶은 경우 PostList 클래스에 template_name = 'blog/index.html'을 추가하면 된다. 필자는 post_list.html을 새로 만들어 index.html의 내용을 복사 붙여넣기 하였다.

하지만 이렇게 해도 실제 포스트 목록 내용이 반영되지 않는다.

앞서 FBV를 사용할 때는 index() 함수에서 Post.objects.all() 함수로 가져온 Post 레코드를 abc 딕셔너리로 명명했다. 그리고 템플릿 파일에서 for 문으로 abc에 담긴 Post 레코드를 하나씩 나열했다.

CBV를 사용하면, PostList 클래스에 단지 model=Post라고 선언하고, 템플릿에서 ListView로 만든 클래스의 모델 객체를 가져오도록 object_list 또는 모델명 소문자_list 명령어를 사용하면 끝이다.

또는 context_object_name을 사용하여 뷰에서 템플릿 파일에 전달하는 컨텍스트 변수명을 지정할 수 있다.

즉, index.html의 내용을 복사 붙여넣기한 post_list.html을 다음과 같이 수정하자.

<!-- blog/templates/blog/post_list.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Blog</title>
</head>
<body>
<h1>Blog</h1>
{% for p in post_list %}    <!-- post_list 라고 수정 -->
    <hr/>
    <h2><a href="{{ p.get_absolute_url }}">{{ p.title }}</a></h2>
    <h4>{{ p.created_at }}</h4>
    <p>{{ p.content }}</p>
{% endfor %}
</body>
</html>

정리하면

  1. ListView를 상속받은 PostList 클래스를 만들고, model = Post로 지정

  2. blog/urls.py를 열어 urlpatterns 수정하여 PostList 클래스로 처리하게 함

  3. PostList 클래스가 자동으로 템플릿을 찾을 수 있게 템플릿 이름을 post_list.html로 변경

  4. FBV를 사용할 때는 index() 함수에서 Post.objects.all() 함수로 가져온 Post 레코드를 abc 딕셔너리로 명명하고, 템플릿 파일에서 for 문으로 abc에 담긴 Post 레코드를 하나씩 나열했지만,
    CBV는 PostList 클래스에 단지 model=Post라고 선언하고, 템플릿에서 ListView로 만든 클래스의 모델 객체를 가져오도록 object_list 또는 모델명 소문자_list 명령어를 사용하면 끝이다.

CBV로 포스트 상세 페이지 만들기

포스트 상세 페이지를 CBV로 만들어보자!!

DetailView로 포스트 상세 페이지 만들기

여러 레코드를 목록 형태로 보여줄 때는 ListView를 사용했다면, 한 레코드를 자세히 보여줄 때는 DetailView를 사용한다!!

blog/views.py를 열어 다음과 같이 수정하자. single_post_page() 함수는 다음처럼 DetailView를 사용하여 단 2줄로 대체할 수 있다. FBV를 활용한 함수들은 더 이상 쓸모 없으니 삭제하거나 주석 처리하자.

# blog/views.py
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from .models import Post


class PostList(ListView):
    model = Post
    ordering = '-pk'


class PostDetail(DetailView):
    model = Post

# def index(request):
#     posts = Post.objects.all().order_by('-pk')
#
#     return render(
#         request,
#         'blog/index.html',
#         {
#             'posts': posts,
#         }
#     )


# def single_post_page(request, pk):
#     post = Post.objects.get(pk=pk)
#
#     return render(
#         request,
#         'blog/single_post_page.html',
#         {
#             'post': post,
#         }
#     )

urls.py 수정하기

blog/urls.py를 열어 '/blog/정수/' 형태의 URL인 경우 PostDetail 클래스를 이용하도록 수정하자.

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

urlpatterns = [
    path('<int:pk>/', views.PostDetail.as_view()),
    path('', views.PostList.as_view()), # 추가
    # path('<int:pk>/', views.single_post_page),
    # path('', views.index),
]

템플릿 파일 지정하기

DetailView는 앞서 ListView를 사용했을 때 처럼 template_name = "blog/my_template.html"처럼 수정하거나, <appname>/<model name>_detail.html 파일을 기본 템플릿으로 사용하도록 설정되어 있다.

즉, 우리는 blog/views.py에서 PostDetail 클래스를 정의하고, model = Post 라고 정의 했기 때문에 post_detail.html 이라는 템플릿이 필요하다.

single_post_page.html의 파일명을 post_detail.html로 바꾸자.

ListView와 마찬가지로, 템플릿에서 DetailView로 만든 클래스의 모델 객체를 가져오려면 object 또는 모델명 소문자를 사용한다.

또는 context_object_name을 사용하여 뷰에서 템플릿 파일에 전달하는 컨텍스트 변수명을 지정할 수 있다.


정리

MTV패턴을 계속 상기하며 개발하기
as_view란?

profile
AI 서비스 엔지니어를 목표로 공부하고 있습니다.

0개의 댓글