---
description: '210724'
---

# FBV (Function Based View)와 CBV (Class Based View)

## FBV (Function Based View)로 블로그 리스트 페이지 만들기

기본적으로 프로젝트 폴더에 있는 `urls.py` 에는 `admin/` 만 등록되어 있다. 따라서 다음과 같이 `127.0.0.1/blog` 주소로 들어가면 404 오류가 난다.

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfGJBDf-f9g4NnjOb_x%2F-MfLAYKzYg8nesiJ3eGc%2Fimage.png?alt=media\&token=70b759f4-09d7-4e5f-8cae-4a3d3b8485d9)

`urls.py` 에 있는 `urlpatterns` 은 이정표의 의미. 나아갈 수 있는 링크들을 의미한다. 여기에 `blog/` 를 추가해주면 된다.

`urls.py`

```python
from django.contrib import admin
from django.urls import path, include

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

* 이 때 include 라는 함수를 import 해줘야 한다
* blog 앱 폴더에는 아직 urls 라는 파일이 없다. 따라서 이를 추가해줘야 한다.

`blog/urls.py`

```python
from django.urls import path
from . import views

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

* 이 때 `views.index` 를 호출하므로 `views.py` 에서 `index` 함수를 선언해줘야 한다.
* 지금처럼 하는 방법이 FBV 방법

`blog/views.py`

```python
from django.shortcuts import render

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

* blog의 index.html을 가지고 렌더링 하라는 뜻
* 따라서 index.html이 또 필요하다.
  * templates 라는 폴더 안에, blog 라는 폴더 안에 index.html을 만든다
  * templates/blog/index.html
  * 앱의 독립성을 위해 이렇게 작업하며 추후에 추가 설명

`blog/templates/blog/index.html`

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



![https://m.blog.naver.com/nuberus/221747636515](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfGJBDf-f9g4NnjOb_x%2F-MfLES7T-mc3VdxsOAxC%2Fimage.png?alt=media\&token=7a9bd51e-b2b7-428f-95d3-6d8d4abe2283)

* 지금까지 한 작업은 클라이언트에서 View로 요청을하고 View가 템플릿을 렌더링 한 후 응답을 준 과정이다.
* 이제 Model에서 DB로 쿼리를 날린 후 데이터를 얻어서 응답을 주는 과정을 해보려고 한다.

`blog/views.py`

```python
from django.shortcuts import render
from .models import Post

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

* 렌더링의 리턴값으로 posts 라는 dictionary를 추가했다.
  * 이 posts는 models.py에 있는 post class이다.

지금부터의 파일들은 특별한 명시가 없으면 모두 blog 앱 내부에 있는 파일이다.

`index.html`

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

    {% for p in posts %}
        <h3>{{ p }}</h3>
    {% endfor %}
</body>
</html>
```

* 10-12
  * html에서 동적언어를 쓸 수 있게끔 장고에서 지원한다.
  * for문을 시작할 때와 끝날 때 \{% %\} 를 써준다.
  * posts는 `views.py`에 있는 dictionary 값으로 'posts' key에 대한 value 값들을 하나씩 \<h3> 형태로 작성되게 한다.

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfGJBDf-f9g4NnjOb_x%2F-MfLFayp9ucdjDaO2IkW%2Fimage.png?alt=media\&token=560efaa2-6ed7-4e64-aa76-affe7a550999)

* 이 때 어떻게 p 를 부르기만 해도 db에 저장된 내용이 다 뜰까?
  * 이유는 `models.py`에 정의되어있는 \_\_str\_\_ 함수 때문

`models.py`

```python
    def __str__(self):
        return f"{self.pk} {self.title}"
```



좀 더 추가해보자.

`index.html`

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

    {% for p in posts %}
        <hr/>
        <h3>{{ p }}</h3>
        <h4>{{ p.create_at }}</h4>
        <p> {{ p.context }}</p>
    {% endfor %}
</body>
</html>
```

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfGJBDf-f9g4NnjOb_x%2F-MfLGOigg94p6ndsd6Ez%2Fimage.png?alt=media\&token=ef304237-f709-44ed-bdba-c0306b5ea779)

또한, posts의 값들을 특정 테이블값으로 정렬할 수 있다.

`views.py`

```python
# pk 순
posts = Post.objects.all().order_by('pk')

# pk 역순
posts = Post.objects.all().order_by('-pk')
```

* 여기서 objects.all()은 쿼리문이라고 생각하면 된다.



## FBV (Function Based View)로 블로그 상세 페이지 만들기

blog 페이지에서 게시글을 눌렀을 때 새로운 페이지로 이동하는 과정을 구성할 것임

`urls.py`

```python
from django.urls import path
from . import views

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

* <> 안에는 자료형을 넣을 수 있으며 pk가 정수로 오기 때문에 다음과 같이 가능
* `views.py` 에 `single_post_page` 함수를 추가할 것임

`views.py`

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

`single_page.html`

```markup
<!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>
    <p>여기에 댓글이 들어올 수 있겠죠?</p>
</body>
</html>
```

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfGJBDf-f9g4NnjOb_x%2F-MfLRdKXK6Mt9N9KR92n%2Fimage.png?alt=media\&token=6330cda1-8680-4d47-8f98-54c164eb2409)

여기서 블로그를 누르면 모든 /blog 로 이동한다.

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfGJBDf-f9g4NnjOb_x%2F-MfLRS6BiLQJO3iWP_Oe%2Fimage.png?alt=media\&token=ac1b2dc6-ceae-4956-87a7-7d2ff16f87db)



`index.html`

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

    {% for p in posts %}
        <hr/>
        <h3>><a href="{{ p.get_absolute_url }}">{{ p }}</a></h3>
        <h4>{{ p.create_at }}</h4>
        <p> {{ p.context }}</p>
    {% endfor %}
</body>
</html>
```

* 여기서 `p.get_absolute_url` 를 호출할 수 있다. 이것은 `model.py` 에서 정의해줘야 하는 함수이지만 장고에서 제공해주는 기능이다.
  * 장고에서 제공하는 기능을 정의해주면 된다는 뜻

`models.py`

```python
    def get_absolute_url(self):
        return f'/blog/{self.pk}'
```

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfGJBDf-f9g4NnjOb_x%2F-MfLSMvf9P6PffJBlMGg%2Fimage.png?alt=media\&token=61a9d6b9-ea22-4a3e-ad03-2289345cbb88)

각 포스트에서 /blog로 이동할 수 있으며 admin 페이지에서도 view on site 기능이 추가되었다.

## FBV로 대문 페이지 자기소개페이지 만들기

127.0.0.1/8000 에서 blog나 admin을 입력하지 않으면 에러가 발생한다. 대문 페이지도 마련해주자.

`urls.py`

```python
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'))
]
```

`single_pages/urls.py`

```python
from django.urls import path
from . import views

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

`single_pages/views.py`

```python
from django.shortcuts import render

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

```

`single_pages/templates/single_pages/landing.html`

```markup
<!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>
```

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfLSSW4_yMhOCWEY-kS%2F-MfLXHITBXpc1g5W9yUk%2Fimage.png?alt=media\&token=d019b331-2c47-4b8e-bfbd-20eba5bfd987)



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

장고에서 이러한 블로그 페이지들을 클래스로 제공하고 있다.

`blog/urls.py`

```python
from django.urls import path
from . import views

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

`blog/views.py`

```python
from django.shortcuts import render
from django.views.generic import ListView
from .models import Post

class PostList(ListView):
    model = Post
    template_name = "blog/index.html"

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

이 때 서버를 실행해보면,

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfLSSW4_yMhOCWEY-kS%2F-MfLbE3MFnPpeuOepBn0%2Fimage.png?alt=media\&token=c6cdb22b-6c72-4acb-be3f-c0fd5b2da2a2)

아무것도 뜨지 않는다. 이는,

`index.html`

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

    {% for p in posts %}
        <hr/>
        <h3>><a href="{{ p.get_absolute_url }}">{{ p }}</a></h3>
        <h4>{{ p.create_at }}</h4>
        <p> {{ p.context }}</p>
    {% endfor %}
</body>
</html>
```

반복문에서 posts에 해당하는 dictionary가 없기 때문이다. 기본적으로 장고에서 제공하는 CBV는 dictionary 변수를 `post_list`로 정의한다. 따라서 이렇게 수정해주면 된다.

```
{% for p in post_list %}
```

![](https://2275555537-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MNN4u9o9noCQwX8pO35%2F-MfLSSW4_yMhOCWEY-kS%2F-MfLbmOdJnxk9H_s4941%2Fimage.png?alt=media\&token=228d9a98-5495-44e7-9d0f-6f8a78b6739c)

또한, views.py 에서 template\_name을 선언해주지 않고 template 파일 이름을 `post_list` 로 변경해주면 된다.

또, 순서도 정해줄 수 있다.

`views.py`

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



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

마찬가지로 single\_page도 클래스로 선언 가능하다.

`views.py`

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

class PostDetail(DetailView):
    model = Post

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

그리고 템플릿 파일 이름을 `post_detail.html` 로 변경한다.

마지막으로, url도 변경해주면 끝.

`urls.py`

```python
from django.urls import path
from . import views

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



장고에서는 CBV로 제공하는 기능이 많기 때문에 편리하게 이용할 수 있고 이것을 직접 구현하기는 소모적이기 때문에 CBV를 사용하는 것을 권장한다!
