포스트 목록 페이지에 카테고리 추가하기
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>