ManyToMany를 이용한 태그 구현

파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트 강의를 듣고 정리한 글입니다.

인스타그램 서비스에서는 포스팅을 할 때 태그를 지원한다.
django-taggit을 이용하면 이러한 태그 기능을 손쉽게 개발할 수 있다. (django-taggit은 실제 장고 코어 개발자도 참여중인 프로젝트라고 한다.)

본 포스팅에서는 학습을 목표로 하기에 ManyToMany필드를 이용해 태그 입력을 구현해본다.

사전 작업


  • instagram앱을 추가 후 settings에 앱을 추가하였다.
  • 프로젝트 레이어의 urls.py에 instagram.urls를 추가하였다.
  • instagram.urls의 app_name은 ‘instagram’으로 지정하였다.

모델


Tag 모델

from django.db import models
from django.urls import reverse

class Tag(models.Model):
    name = models.CharField(max_length=50, unique=True)

    def __str__(self):
        return self.name

Post 모델

Tag모델을 ManyToMany필드로 추가한다.

extract_tag_list 함수를 통해 caption으로부터 Tag 추출 작업을 수행한다.

from django.conf import settings
from django.db import models
from django.urls import reverse
import re

class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    photo = models.ImageField(upload_to='instagram/post/%Y/%m/%d')
    caption = models.CharField(max_length=500)
    tag_set = models.ManyToManyField('Tag', blank=True)
    location = models.CharField(max_length=100)

    def __str__(self):
        return self.caption

    def extract_tag_list(self):
        tag_list = []
        for tag_name in re.findall(r'#([a-zA-Z\dㄱ-힣]+)', self.caption):
            tag, _ = Tag.objects.get_or_create(name=tag_name)
            tag_list.append(tag)
        return tag_list

    # def get_absolute_url(self):
    #     return reverse('')


모든 필드가 아닌 유저 폼에서 입력받을 폼만 추가한다.
1. tag: 뷰에서 캡션으로 부터 뽑아서 저장 할 것이다.
2. author: 뷰에서 인증 데이터를 확인 후 해당 유저로 저장 할 것이다.

from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['photo', 'caption', 'location']
        widgets = {
            'caption': forms.Textarea
        }


post.tag_set.add: 입력받은 태그 객체 리스트를 모두 해당 post에 대한 태그로 추가한다.

post_tag_set.add를 시작하기 전에 post.save()를 먼저 수행해야 한다. add에 태그 객체 리스트를 입력할 때 post의 pk가 존재해야 하기 때문이다.

from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect

from instagram.forms import PostForm
from instagram.models import Tag

@login_required
def post_new(request):
    if request.method == 'POST':
        form = PostForm(request.POST, request.FILES)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user  # 유저 저장
            post.save()  # tag_set에 add하기 전에 수행 (PK가 존재해야 한다.)
            post.tag_set.add(*post.extract_tag_list())  # 태그 추가
            messages.success(request, '포스팅을 저장했습니다.')
            return redirect('/')  # TODO: get_absolute_url 활용
    else:
        form = PostForm()
    return render(request, 'instagram/post_form.html', {
        'form': form,
    })

데이터베이스

instagram_post, instagram_tag테이블에는 저장되는 정보가 없다.
다만 instagram_post_tag_set이라는 중간 테이블이 생성된다.

instagram_post_tag_set 테이블은 post_id와 tag_id를 연결해주는 역할을 한다.

instagram_post

instagram_post_tag_set

instagram_tag

좋은 웹페이지 즐겨찾기