포스트 목록 페이지에 카테고리 추가하기

Posted on 2021-08-25 by GKSRUDTN99
Django로 웹사이트 만들기 장고

테스트 코드 작성하기

setUp()안에 Category 레코드를 2개 생성하고, 포스트도 setUp()에서 미리 3개 만든다.

# blog/tests.py
from django.test import TestCase, Client
from bs4 import BeautifulSoup
from .models import Post, Category
from django.contrib.auth.models import User

class TestView(TestCase):
    def setUp(self):
        self.client = Client()
        self.user_trump = User.objects.create(username='trump', password='somepassword')
        self.user_obama = User.objects.create(username='obama', password='somepassword')

        self.category_programming = Category.objects.create(name='programming', slug='programming')
        self.category_music = Category.objects.create(name='music', slug='music')

        self.post_001 = Post.objects.create(
            title='첫 번째 포스트입니다.',
            content='Hello World. We are the world.',
            category=self.category_programming,
            author=self.user_trump,
        )
        self.post_002 = Post.objects.create(
            title='두 번째 포스트입니다.',
            content='1등이 전부는 아니잖아요!',
            category=self.category_music,
            author=self.user_obama,
        )
        self.post_003 = Post.objects.create(
            title='세 번째 포스트입니다',
            content='category가 없을수도 있죠',
            author=self.user_obama,
        )

(...생략...)

카테고리 카드는 포스트 목록, 상세 페이지 등에서 모두 존재하므로, category_card_test()를 따로 정의한다.

# blog/tests.py
...
    def category_card_test(self, soup):
        categories_card = soup.find('div', id='categories-card')
        self.assertIn('Categories', categories_card.text)
        self.assertIn(f'{self.category_programming.name} ({self.category_programming.post_set.count()})', categories_card.text)
        self.assertIn(f'{self.category_music.name} ({self.category_music.post_set.count()})', categories_card.text)
        self.assertIn(f'미분류 (1)', categories_card.text)
...

이전의 test_post_list()는 게시물이 없는 경우를 먼저 테스트 한 뒤, 게시물을 새로 만들어 게시물이 있는 경우를 이어서 테스트 하였다.

이제 post를 setUp()에서 생성하므로, 이를 대폭 수정한다.

# blog/tests.py
    def test_post_list(self):
        # 포스트가 있는 경우
        self.assertEqual(Post.objects.count(), 3)

        response = self.client.get('/blog/')
        self.assertEqual(response.status_code, 200)
        soup = BeautifulSoup(response.content, 'html.parser')

        self.navbar_test(soup)
        self.category_card_test(soup)

        main_area = soup.find('div', id='main-area')
        self.assertNotIn('아직 게시물이 없습니다', main_area.text)

        post_001_card = main_area.find('div', id='post-1')
        self.assertIn(self.post_001.title, post_001_card.text)
        self.assertIn(self.post_001.category.name, post_001_card.text)
        self.assertIn(self.user_trump.username.upper(), main_area.text)

        post_002_card = main_area.find('div', id='post-2')
        self.assertIn(self.post_002.title, post_002_card.text)
        self.assertIn(self.post_002.category.name, post_002_card.text)
        self.assertIn(self.user_obama.username.upper(), main_area.text)

        post_003_card = main_area.find('div', id='post-3')
        self.assertIn(self.post_003.title, post_003_card.text)
        self.assertIn('미분류', post_003_card.text)
        self.assertIn(self.user_obama.username.upper(), main_area.text)

        # 포스트가 없는 경우
        Post.objects.all().delete()
        self.assertEqual(Post.objects.count(), 0)
        response = self.client.get('/blog/')
        soup = BeautifulSoup(response.content, 'html.parser')
        main_area = soup.find('div', id='main-area')
        self.assertIn('아직 게시물이 없습니다', main_area.text)

base.html 수정

수정한 테스트에 맞게, base.html에 categories_card id를 부여한다.

<!-- blog/templates/blog/base.html -->
...
<!-- Categories widget-->
            <div class="card mb-4" id="categories-card">
                <div class="card-header">Categories</div>
                <div class="card-body">
                    <div class="row">
                        <ul class="list-unstyled mb-0">
                            <li><a href="#!">Web Design</a></li>
                            <li><a href="#!">HTML</a></li>
                            <li><a href="#!">Freebies</a></li>
                        </ul>
                    </div>
                </div>
            </div>
...

get_context_data() 메서드로 category 관련 인자 넘기기

이전에 PostList가 상속한 ListView는 get_context_data()함수를 내장하고 있다.

단지 model=Post라고 선언하면 get_context_data()는 자동으로 post_list = Post.objects.all()을 명령한다.

이 함수를 오버라이딩 하여 카테고리에 대한 정보도 가져오도록 할 것이다.

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


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

    def get_context_data(self, **kwargs):
        context = super(PostList, self).get_context_data()
        context['Categories'] = Category.objects.all()
        context['no_category_post_count'] = Post.objects.filter(category=None).count()
        return context
(...생략...)

템플릿 수정

base.html이 카테고리를 잘 표시하도록 수정한다.

<!-- blog/templates/blog/base.html -->
<!-- Categories widget-->
            <div class="card mb-4" id="categories-card">
                <div class="card-header">Categories</div>
                <div class="card-body">
                    <div class="row">
                        <ul class="list-unstyled mb-0">
                            {% for category in categories %}
                                <li>
                                    <a href="{{ category.get_absolute_url }}">{{ category }}</a>
                                </li>
                            {% endfor %}
                            <li>
                                <a href="/blog/category/no_category/">미분류 ({{ no_category_post_count }})</a>
                            </li>
                        </ul>
                    </div>
                </div>
            </div>

테스트에서 포스트 카드를 post-1 같은 id로 찾으므로, post_list.html을 이에 맞게 수정한다.

또한, 포스트 카드에 카테고리를 표시하기로 했으므로 이도 추가한다.

<!-- blog/templates/blog/post_list.html -->
<!-- Blog Post -->
            <div class="card mb-4" id="post-{{ p.pk }}">
                <a href="#!">
                    {% if p.head_image %}
                        <img class="card-img-top" src="{{ p.head_image.url }}"
                             alt="{{ p }}"/>
                    {% else %}
                        <img class="card-img-top" src="https://picsum.photos/seed/{{ p.id }}/800/200"
                             alt="random_image">
                    {% endif %}
                </a>
                <div class="card-body">
                    <div class="small text-muted">
                        Posted on {{ p.created_at }} by
                        <a href="#">{{ p.author | upper }}</a>
                        {% if p.category %}
                        <span class="badge bg-secondary float-end">{{ p.category }}</span>
                        {% else %}
                        <span class="badge bg-secondary float-end">미분류</span>
                        {% endif %}
                    </div>
                    <h2 class="card-title">{{ p.title }}</h2>
                    {% if p.hook_text %}
                        <h5 class="text-muted">{{ p.hook_text }}</h5>
                    {% endif %}
                    <p class="card-text">{{ p.content | truncatewords:45 }}</p>
                    <a class="btn btn-primary" href="{{ p.get_absolute_url }}">Read more →</a>
                </div>
            </div>