Category 모델 만들기
포스트 목록과 상세 페이지에 공통적으로 카테고리가 보여야 하며 해당 카테고리를 클릭 할 시 관련된 포스트가 나열되어야 한다.
models.py에 Category 모델 만들기
Category 모델에 name, slug 라는 이름의 필드를 만들었으며 unique=True로 동일한 name을 갖는 카테고리를 만들 수 없게 하였다. slug필드는 고유 URL을 만들때 주로 사용하는 것으로 Post 모델처럼 pk를 활용해 URL을 만들 수 있으며 allow_unicode=True로 한글을 지원하도록 한다.
Post 모델에 category 필드 추가하고 migration
Post 모델에 Category를 ForeignKey로 새로운 필드를 추가한다. 미분류 포스트도 있을 수 있으므로 null=Ture로 하며 관련 카테로기가 삭제된 경우 포스트까지 삭제되지 않고 해당 카테고리 필드만 null 이 되도록 on_delete=models.SET_NULL로 설정하고 마무리로 migration한다.
admin.py에 Category 모델 등록
admin 페이지에서 카테고리를 관리할 수 있도록 admin.py에 관련 코드를 추가하도록 한다. Category 모델을 import 하고 CategoryAdmin이라는 클래스를 추가한다. 그 내용으로 prepopulated_fields = {'slug': ('name0', )}으로 name 필드에 값 입력시 자동으로 slug가 만들어지도록 한다. admin.site.register(Category, CategoryAdmin)으로 admin 페이지에서 사용할 수 있도록 등록한다.
Meta로 모델의 복수형 알려주기
Django에서 admin 페이지에서 제공하는 category기능은 정말 편리하지만 위의 빨간 박스에서 알 수 있듯이 categorys로 복수형 표현이 잘못되었다. class Category안에 class Meta로 Categories로 수정한다.
카테고리를 빈 칸으로 남겨두기
간혹 가다가 카테고리를 설정하기 애매하거나 그냥 하기 싫을때가 있다. 별도의 카테고리를 만들면서 별도의 카테고리 설정 없이 포스트 할 수 없는것이 현재 상황이므로 카테고리를 빈 칸으로 남길 수 있게 models.py 파일을 수정한 후 migration 한다.
셸 플러스를 이용해 DB 살펴보기
Django 셸 이용
python manage.py shell 명령어로 Django shell을 연다. 장고 셸은 장고의 기능과 장고로 개발한 내용을 불러올 수 있다.
from blog.models import Post, Category로 Post 모델과 Category 모델을 불러오고 Post.objects.count() 와 Category.objects.count()로 각각 몇개의 포스트, 카테고리가 있는지 확인한다.
for 문으로 DB에 저정된 모든 Post, Category 레코드 출력이 가능하다. 장고 셸에서 나가려면 exit()를 입력한다.
셸 플러스 열기
셸 플러스는 따로 설치가 필요한 요소이다. pip install django_extension을 설치 후 pip install ipython으로 ipython을 설치한다. 바로 이용할 수 없으니 settings.py의 INSTALLED_APPS 리스트에 django_extensions를 추가한다.
그 후 python manage.py shell_plus 명령어를 입력하면 셸 플러스가 실행된다. 앞으로도 편리한 기능의 shell plus만 쓰게 될 것이다. 따로 import 할 필요가 없이 자동으로 해 주고 for 문 사용시 자동으로 들여쓰기를 제공해 주는 편리한 셸이다.
포스트와 카테고리 목록 출력
셸 플러스로 DB에 저장된 포스트와 카테고리를 출력해 본다.
in[3] 에다가 slug 필드값이 'Programing'인 카테고리를 category_programming에 저장한다.
in[5] 에는 name__startswith()='Culture'로 name 필드값이 'Culture'로 시작하는 카테고리를 category_culture에 저장한다.
in[7], in[8]은 각각 카테고리에 해당하는 포스트 목록을 출력한다. programming.post_set으로 category_programming에 저장되어 있는 category 레코드와 ForeignKey로 연결된 Post 레코드를 불러온다 이때 소문자_set 이 기본 설정이니 대문자로 쓰는 일이 없도록 한다.
이렇게 자꾸 존재하지 않는다고 하는데... slug는 대문자가 아니라 소문자로 표현되는걸 깜빡하고 자꾸 대문자로 쓰니 인식을 하지 못하고 오류를 출력하는 것이였다.
정상적으로 입력하고 출력하면 다음과 같다.
카테고리가 programming 였어야 했는데 오타로 programing 가 되어있다. 함부로 바꾸면 오류가 날까 두려우니 그냥 내버려 두기로 하였다.
post_list 수정
페이지 모양 구상 후 테스트 코드 작성
post_list 페이지 부분 오른쪽에 카테고리 카드의 위치를 잡고 카테고리에 속한 포스트의 개수를 괄호안에 표시 되게끔 한다. 포스트 요약 부분에 카테고리를 뱃지 모양으로 작게 표시한다. 테스트 코드는 다음과 같다. 맨위에 Category를 import 하는것을 까먹지 않도록 한다.
테스트를 위한 카테고리 2개를 만들어 본다.
카테고리 뿐만 아니라 포스트 + 카테고리 매개변수를 추가한다. 세 번째 포스트는 카테고리가 없다.
category_card_test() 함수 따로 만들기
오른쪽 부분에 위치할 카테고리 카드 부분을 테스트 하기 위해 테스트 코드를 작성한다. id가 categories_card인 div 요소를 찾아서 Categories 문구, 모든 카테고리 출력, 미분류 카테고리의 항목에 ()와 같은 괄호가 써져 있는지 테스트 한다.
test_post_list() 함수 수정
크게 포스트가 있는 경우와 없는 경우로 나눠서 테스트 코드를 작성한다.
포스트가 있는 경우
포스트가 없는 경우
div 요소에 id 부여
위의 테스트 코드를 실행하면 categories_card가 None라며 오류를 출력하는데 아직 관련 html 파일에 id를 부여하지 않았기 때문이다. base.html 파일을 수정한다.
get_context_data() 메서드로 category 관련 인자 넘기기
base.html 파일 수정으로 categories_card 가 None라는 오류는 없어졌지만 대신에 programming(1)이 없어서 테스트 코드를 통과하지 못하였다. blog/views.py에서 Category 레코드를 가져와 post_list.html에 반영하여 해결해 본다.
ListView, DatailView 같은 클래스는 get_context_data 메서드를 내장하고 있다고 한다. 따라서 ListView를 상속받은 PostList 클래스에 model = Post 라고 선언하면 get_context_data에서 자동으로 post_list = Post.objects.all()를 명령한다고 한다. 그렇기 때문에 빨간 박스안에 따로 Post.objects.all()를 입력하지 않은 것이다.
카테코리와 카테고리가 없는 경우를 쿼리셋을 딕셔너리 형태로 만들었다. Category.objects.all()를 통해 딕셔너리의 'categories' 키에 연결해 담고, 아래 코드는 카테고리가 없는 포스트의 수를 담기위해 filter를 사용하였다.
template 수정 후 웹 브라우저에서 확인
views.py파일의 PostList 부분에 코드를 추가했으므로 본래대로라면 post_list.html을 수정해야 하겠지만 모듈화를 하였고 카테고리 관련 부분은 base.html 파일에 있으므로 거기에 코드를 추가한다.
for 문으로 categories 요소들을 <li> 태그 목록 형태로 출력시키게끔 했으며 get_absolute_url로 카테고리 고유의 URL을 만들었고 "/blog/category/no_category"에 미지정 카테고리가 붙은 포스트만 모을 수 있도록 하였다.
post_list.html의 <div class="card mb-4">에 id를 부여하여 테스트 코드를 통과하도록 한다.
id="post-1" 인 요소가 있으나 안에 programming이 없어 테스트에 통과하지 못 했다.
<span>태그를 추가 후 bootstrap에서 제공하는 badge를 이용해 카테고리 표현해 본다. 여기서 class의 float-right는 badge를 오른쪽 끝에 위치하라는 뜻이다.
이번에는 미분류 문구가 없으므로 테스트에 통과하지 못 하였다. if 문을 이용하여 해결한다.
'Backend > Django + Bootstrap 개발 일지' 카테고리의 다른 글
19. 다대다 관계 구현(19 ~ 22) (Tag 모델, 포스트 목록과 상세 페이지에 태그 기능 추가, 태그 페이지 구현 ) (0) | 2022.08.08 |
---|---|
18. 다대일 관계 구현 (카테고리 기능 2) (0) | 2022.08.07 |
16. 다대일 관계 구현(16 ~ 18) (작성자 추가) (0) | 2022.07.29 |
15. 템플릿 모듈화 (0) | 2022.07.27 |
14. 테스트 주도 개발 적용 (0) | 2022.07.25 |