Django 태깅 기능 추가하기

Kangjik Kim·2025년 2월 9일
0

블로그의 일반적인 기능으로 태그를 사용해 게시물을 분류하는 기능이 있다.
태그를 사용하면 간단한 키워드를 사용해 컨텐츠를 비계층적으로 분류할 수 있다.
태그는 단순히 게시글에 할당할 수 있는 레이블 또는 키워드다.
서드파티 쟝고 태깅 애플리케이션을 프로젝트에 통합해 태깅 시스템을 만들어보자.

django-taggit 은 주로 Tag 모델과 관리자를 제공해 모든 모델에 태그를 쉽게 추가할 수 있게 해 주는 재사용 가능한 애플리케이션이다.

먼저 아래의 명령어를 통해 django-taggit 을 설치해보자.

pip install django-taggit==3.0.0

그 다음 settings.py파일의 INSTALLED_APPS 설정에 아래와 같이 taggit을 추가한다.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog.apps.BlogConfig',
    'taggit',
]

blog의 models.py파일을 열고 다음 코드와 같이 django-taggit에서 제공하는 TaggableManager 관리자를 Post 모델에 추가한다.

from taggit.managers import TaggableManager

class Post(models.Model):
		...
    tags = TaggableManager()

이제 tags 관리자를 사용해 태그를 추가, 검색 및 제거할 수 있다.

Tag 모델은 태그를 저장하는 데 사용된다. 이 모델은 nameslug 필드를 가지고 있다.

TaggedItem 모델은 관련 태깅된 객체를 저장하는 데 사용되는데, 관련 Tag 객체의 Foreign Key 필드가 있다. 또 ContentType 객체의 ForeignKey 와 태그가 지정된 객체의 id를 저장하기 위한 IntegerField 가 있다. content_typeobject_id 필드를 결합해 프로젝트의 모든 모델과 일반화한 관계를 형성할 수 있는데, 이렇게 하면 Tag 인스턴스와 애플리케이션의 다른 모델의 인스턴스 관계를 만들 수 있다.

마이그레이션을 진행한 후, 이제 태그 관리자를 사용하는 방법을 살펴보자.

python manage.py makemigrations
python manage.py migrate
python manage.py shell

다음 코드를 실행해 id가 1인 게시글 하나를 조회한다.

>>> from blog.models import Post
>>> post = Post.objects.get(id=1)

그 다음 일부 태그를 추가하고 태그를 검색해 성공적으로 추가되었는지 확인한다.

>>> post.tags.add('music', 'jazz', 'django')
>>> post.tags.all()
<QuerySet [<Tag: django>, <Tag: music>, <Tag: jazz>]>

끝으로 태그를 제거하고 태그 목록을 다시 확인한다.

>>> post.tags.remove('django')
>>> post.tags.all()
<QuerySet [<Tag: music>, <Tag: jazz>]>

post.tags 관리자를 사용해 모델에서 태그를 추가, 조회, 제거하는 과정은 위와 같이 간단하다.

다음으로 개발 서버를 시작해 브라우저에서 관리페이지의 Tags 객체들을 살펴보자.

http://127.0.0.1:8000/admin/

Tags에 들어가면 아래와 같은 태그의 목록을 확인할 수 있다.

jazz 태그를 눌러 들어가보자.

Tagged Item에서 blog의 post의 2번 게시글이 이 태그를 사용중인 것을 확인할 수 있다.

이제 post모델의 2번 게시글로 들어가 Tags필드를 확인해보자.

이제 태그를 표시하도록 블로그 게시글을 편집해보자.
blog/post/list.html 을 편집해 tag 관련 HTML 코드를 추가한다.

<p class="tags">Tags: {{ post.tags.all|join:", " }}</p>

join 템플릿 필터는 파이썬 문자열 join() 메서드와 동일하게 동작하는데, 요소들을 지정된 문자열로 연결한다.

브라우저에서 blog 메인 페이지로 이동해보면 아래와 같이 제목 아래에 태그 목록이 나타나게 된다.

다음으로 사용자가 특정 태그로 태깅된 게시글들을 조회할 수 있도록 post_list 뷰를 편집해보자.

blog의 views.py파일을 열어 django-taggit에서 Tag 모델을 불러오고 필요에 따라 게시글을 태그별로 필터링하도록 post_list 뷰를 편집해보자.

from taggit.models import Tag

def post_list(request, tag_slug=None):
    post_list = Post.published.all()
    tag = None
    if tag_slug:
        tag = get_object_or_404(Tag, slug=tag_slug)
        post_list = post_list.filter(tags__in=[tag])

    paginator = Paginator(post_list, 3)
    page_number = request.GET.get('page', 1)
    try:
        posts = paginator.page(page_number)
    except PageNotAnInteger:
        posts = paginator.page(1)
    except EmptyPage:
        posts = paginator.page(paginator.num_pages)
    return render(request, 'blog/post/list.html', {'posts': posts, 'tag': tag}

post_list 뷰의 동작 방식은 아래와 같다.

  1. 기본 값이 Nonetag_slug 매개 변수를 사용한다.
    이 매개 변수는 URL로 전달된다.
  2. 뷰에서 기본 QuerySet을 만들어 게시된 모든 게시글을 조회하고 지정된 태그 슬러그가 있는 경우 get_object_or_404() 함수를 사용해 지정된 슬러그로 Tag 객체를 찾는다.
  3. 주어진 태그가 포함된 게시글을 기준으로 게시글 목록을 필터링한다.
    다대다 관계이기 때문에 주어진 목록에 포함된 태그를 기준으로 게시글을 필터링해야 한다.
    이 경우에는 목록에 하나의 태그만이 포함되어 있다.
    __in 필드 검색을 사용한다.
  4. render() 함수에서 새로운 tag 변수를 템플릿에 전달한다.

QuerySet은 필요시에 실행되므로, 게시글을 검색하기 위한 쿼리셋은 템플릿을 렌더링할 때나 게시글 목록을 반복할 때만 실행된다.

blog의 urls.py파일의 클래스 기반 PostListView URL패턴을 주석처리하고 기존 post_list 뷰의 주석 처리를 해제한다.

 path('', views.post_list, name='post_list'),
	# path('', views.PostListView.as_view(), name='post_list'),

게시글을 태그별로 나열하기 위해 아래의 URL 패턴을 추가해준다.

path('tag/<slug:tag_slug>/', views.post_list, name='post_list_by_tag'),

두 패턴이 동일한 뷰를 가리키지만 이름이 다르다.
첫 번쨰 패턴은 매개 변수 없이 post_list 뷰를 호출하지만, 두 번째 패턴은 tag_slug 매개 변수를 사용해 뷰를 호출한다. slug path converter를 사용해 슬러그 타입 문자열로 매개 변수를 매칭시킨다.

post_list 뷰를 사용하고 있으니 게시글 목록 템플릿을 수정해 페이징에서 posts 객체를 사용하도록 바꿔주자.

{% include "pagination.html" with page=posts %}

아래의 코드를 게시글 목록 페이지의 h1 아래에 추가한다.

특정 태그로 태깅된 게시글로 필터링할 경우 지정된 태그를 표시하기 위함이다.

{% if tag %}
    <h2>Posts tagged with "{{ tag.name }}"</h2>
{% endif %}

기존 태그 목록을 표시하던 부분을 아래와 같이 바꿔주자.

<p class="tags">
        Tags:
        {% for tag in post.tags.all %}
            <a href="{% url "blog:post_list_by_tag" tag.slug %}">
                {{ tag.name }}
            </a>
            {% if not forloop.last %}, {% endif %}
        {% endfor %}
    </p>

게시글에 태깅된 모든 태그들을 반복하면서 해당 태그로 게시글들을 필터링 할 수 있는 커스텀 URL 링크를 표시하도록 했다. URL 이름과 같이 태그의 slug를 매개 변수로 {% url "blog:post_list_by_tag*"* tag.slug %} 와 같이 URL을 만든다.

게시글 목록에서 태그를 클릭해보면,
아래와 같이 클리된 태그로 필터링된 게시글 목록이 표시되게 된다.

0개의 댓글