taggit 오픈소스를 사용해 tag기능을 추가해보자!!
GettingStart django-taggit
django tag기능 공식 문서 사이트
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'bookmark.apps.BookmarkConfig',
'blog.apps.BlogConfig',
'taggit.apps.TaggitAppconfig',
'taggit_templatetags2',
]
TAGGIT_CASE_INSENSITIVE = TRUE
TAGGIT_LIMIT = 50
from django.db import models
from django.urls import reverse
from taggit.managers import TaggableManager
#중략
modify_dt = models.DateTimeField('MODIFY DATE', auto_now=True)
tags = TaggableManager(blank=True)
#중략
TaggableManager를 import한 후, 기존 Post 테이블에 tags 필드를 추가한다.
👩💻 TaggitManager에 대해서
django-taggit 패키지에 taggit 앱이 들어있고, taggit 앱의 디렉터리 하위의 manager.py 파일에 TaggableManager 클래스가 정의되어있다.
admin.py
from django.contrib import admin
from blog.models import Post
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('id', 'title', 'modify_dt', 'tag_list')
list_filter = ('modify_dt',)
search_fields = ('title', 'content')
prepopulated_fields = {'slug':('title',)}
def get_queryset(self, request):
return super().get_queryset(request).prefetch_related('tags')
def tag_list(self, obj):
teturn ', '.join(o.name for o in obj.tags.all())
list_display에 'tag_list'를 추가한 후, 메서드를 추가한다.
👩🏫 여기서 왜 두 메서드를 추가해야할까?
Post 레코드 리스트를 가져오는 get_queryset() 메소드를 오버라이딩 해야한다. 왜냐하면, Post 테이블과 Tag테이블이 ManyToMany 관계이므로, Tag 테이블의 관련 레코드를 한 번의 쿼리로 미리 가져오기 위함이다.
다대다 관계에서 쿼리 횟수를 줄여 성능을 높이고자 할 때 prefetch_related()메소드를 사용한다.
tag_list 항목에 보여줄 내용을 정의한다.
Taggit app의 테이블이 생성되었다.
Post테이블에 Tags칼럼이 추가된 것을 확인할 수 있다.
tag테이블과 taggedIte테이블이 생성되었다.
여기까지가 db에 관련 세팅
makemigrations/migrate
#/blog/tag/
path('tag/', views.TagCloudTV.as_view(), name='tag_cloud'),#TemplateView를 상속받아 정의
#/blog/tag/tagname/
path('tag/<str:tag>/', views.TaggedObjectLV.as_view(), name='tagged_object_list'),#ListView를 상속받아 정의
/blog/tag/ 요청을 처리할 url을 만들어주고 view의 TagCloudTV로 연결
/blog/tag/tag변수 요청을 처리할 url을 만들어주고 view의 TagObjectLV로 연결
#TemplateView를 추가로 import한다.
from django.views.generic import ListView, DetailView,TemplateView
TemplateView 클래스형 제네릭 뷰를 임포트한다.
👩💻 짚고 넘어가기
제네릭 뷰와 관련해서 모르는 것이 많다.
어떤 메서드에는 TemplateView를 어떤 메서드에는 ListView를 매개변수로 넣어주는데 그 기준이 무엇인지, 제네릭뷰의 종류, 제네릭뷰를 왜 사용하는 것이고 어떻게 사용하는 것인지 알아보고 수정할 것!
class TagCloudTV(TemplateView):
template_name - 'taggit/taggit_cloud.html'
class TaggedObjectLV(ListView):
template_name = 'taggit/taggit_post_list.html'
model = Post
def get_queryset(self):
return Post.objects.filter(tags_name=self.kwargs.get('tag'))
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['tagname'] = self.kwargs['tag']
return context
👩💻 짚고 넘어가기
컨텍스트 변수를 템플릿 파일에 전달하기 위한 메서드라는 것은 알겠는데...
아마 ListView에 정의되어 있는 get_context_data()를 오버라이딩해서 super().get_context_data()를 호출해서 컨텍스트 변수를 구한 후, url에서 'tag'라는 파라미터 값으로 넘어온 값을 'tagname'이라는 이름으로 컨텍스트에 추가한다는 의미인 것 같다. --> 확인해보기
post_detail.html
<br>
<div>
<b>TAGS</b> <i class="fas fa-tag"></i>
{% load taggit_templatetag2_tags %} <!--taggit_templatetag2_tags 모듈을 가져온다-->
{% get_tags_for_object object as "tags" %} <!-- ** -->
{% for tag in tags %}
<a href="{% url 'blog:tagged_object_list' tag.name %}">{{tag.name}}</a>
{% endfor %}
 
<a href="{% url 'blog:tag_cloud' %}"><span class="btn btn-info btn-sm">TagCloud</span></a>
</div>
👉 object 객체(PostDV클래서형 뷰에서 넘겨주는 컨텍스트 변수로 특정 Post객체가 담겨있음)에 달려 있는 태그들의 리스트를 추출해서 tags템플릿 변수에 할당한다.
blog/taggit/taggit_cloud.html
{% extends "base.html" %}
{% block title %}taggit_cloud.html{% endblock %}
{% block extra-style %}
<style>
.tag-cloud{
width: 40%;
margin-left: 30px;
text-align: center;
padding: 5px;
border: 1px solid orange;
background-color: #ffc;
}
.tag-1 {font-size: 12px}
.tag-2 {font-size: 14px}
.tag-3 {font-size: 16px}
.tag-4 {font-size: 18px}
.tag-5 {font-size: 20px}
.tag-6 {font-size: 24px}
</style>
{% endblock %}
{% block content %}
<h1>Blog Tag Cloud</h1>
<br>
<div class="tag-cloud">
{% load taggit_templatetags2_tags %}
{% get_tagcloud as tags %} <!--모든 태그 추출해서 tags변수에 할당-->
{% for tag in tags %}
<span class="tag-{{tag.weight|floatformat:0}}"> <!-- style 관련-->
<a href="{% url 'blog:tagged_object_list' tag.name %}"> {{tag.name}}({{tag.num_times}})</a>
</span>
{% endfor %}
</div>
{% endblock %}
🧐 태그 객체와 관련된 속성의 의미
- {{tag.name}}
: 태그의 이름- {{tag.num_times}}
: 태그가 사용된 횟수- {{tag.weight}}
: 태그의 중요도. tag_num_times의 값을 미리 정해놓은 값에 따라 1.0~6.0사이의 수로 변환한 수치- {{tag.weight|floatformat:0}}
: '|' 오른쪽의 값은 필터의 역할을 한다. tag.weight를 반올림해서 정수형으로 바꾼 값을 의미한다.
taggit_post_list.html
{% extends "base.html" %}
{% block title %}taggit_post_list.html{% endblock %}
{% block content %}
<h1>Posts for tag - {{ tagname }}</h1>
<br>
{% for post in object_list %} <!-- object_list 객체는 TaggedObjectLV 클래스형 뷰에서 넘겨주는 컨텍스트 변수로서 Post 리스트가 담겨있음-->
<h2><a href='{{ post.get_absolute_url }}'>{{ post.title }}</a></h2>
{{ post.modify_dt|date:"N d, Y" }}
<p> {{ post.description }}</p>
{% endfor %}
{% endblock %}
게시물 하단에 태그가 병렬로 나열된다.
django라는 태그를 클릭하면
이렇게 해당 태그를 가지고있는 게시물들을 모아서 출력해준다.
이전 페이지의 TagCloud버튼을 클릭하면,
등록되어있는 태그 목록을 보여주는데 글씨 크기로 태그 등록 순위를 알 수 있다.
오픈소스를 사용해서 tag기능을 만들었다. 전체적인 웹의 구조는 어느정도 이해한 것 같다. 이제 세부적으로 taggit앱을 가져와서 화면에 적용시키는 것 까지의 과정을 여러번 보면서 익숙하게 만들어야겠다.
참고한 블로그
👆 한가지 더 👆
'_'과 '__'은 코드상으로 구분하기 정말 어렵다. 주석으로 상기시켜주면 좋을 것 같다.