태그 선택란 추가하기
Posted on 2021-08-25 by GKSRUDTN99
Django로 웹사이트 만들기
장고
장고에서 기본적으로 제공하는 ManyToMany Field에 대한 Form은 새로운 태그를 추가하기는 어렵다.
태그를 텍스트로 직접 입력할 수 있도록 구현해보자.
템플릿 파일에 input 추가하기
{{ form }}
밑에 tags 필드를 추가한다.
<!-- post_form.html -->
{% extends 'blog/base_full_width.html' %}
{% block head_title %}Create Post - Blog{% endblock %}
{% block main_area %}
<h1>Create New Post</h1>
<hr/>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<table>
{{ form }}
<tr>
<th><label for="id_tags_str">Tags:</label></th>
<td><input type="text" name="tags_str" id="id_tags_str"></td>
</tr>
</table>
<button type="submit" class="btn btn-primary float-end">Submit</button>
</form>
{% endblock %}
테스트 코드 작성하기
쉼표와 세미콜론으로 구분된 태그들을 추가한다.
이미 존재하는 태그는 추가하지 않고, 새롭게 추가된 태그들만 추가되어야 한다.
# tests.py
def test_create_post(self):
# 로그인 하지 않으면 status code가 200이면 안된다!
response = self.client.get('/blog/create_post/')
self.assertNotEqual(response.status_code, 200)
# staff가 아닌 trump가 로그인을 한다.
self.client.login(username='trump', password='somepassword')
response = self.client.get('/blog/create_post/')
self.assertNotEqual(response.status_code, 200)
# staff인 obama로 로그인한다.
self.client.login(username='obama', password='somepassword')
response = self.client.get('/blog/create_post/')
self.assertEqual(response.status_code, 200)
soup = BeautifulSoup(response.content, 'html.parser')
self.assertEqual('Create Post - Blog', soup.title.text)
main_area = soup.find('div', id='main-area')
self.assertIn('Create New Post', main_area.text)
tag_str_input = main_area.find('input', id='id_tags_str')
self.assertTrue(tag_str_input)
self.client.post(
'/blog/create_post/',
{
'title': 'Post form 만들기',
'content': "Post Form 페이지를 만듭시다.",
'tags_str': 'new tag; 한글 태그, python'
}
)
last_post = Post.objects.last()
self.assertEqual(last_post.title, "Post form 만들기")
self.assertEqual(last_post.author.username, 'obama')
self.assertEqaul(last_post.tags.count(), 3)
self.assertTrue(Tag.objects.get(name='new tag'))
self.assertTrue(Tag.objects.get(name='한글 태그'))
self.assertEqual(Tag.objects.count(), 5)
views.py 수정하기
post_form에 추가한 name='tags_str'인 input 요소에 입력된 값을 가져오기 위해 form_valid()에 기능을 추가한다.
# views.py
from django.utils.text import slugify
...
class PostCreate(LoginRequiredMixin, UserPassesTestMixin, CreateView):
model = Post
fields = ['title', 'hook_text', 'content', 'head_image', 'file_upload', 'category']
def test_func(self):
return self.request.user.is_superuser or self.request.user.is_staff
def form_valid(self, form):
current_user = self.request.user
if current_user.is_authenticated and (current_user.is_staff or current_user.is_superuser):
form.instance.author = current_user
response = super(PostCreate, self).form_valid(form)
# tags_str이라는 이름을 가진 post 데이터를 가져온다.
tags_str = self.request.POST.get('tags_str')
if tags_str:
tags_str = tags_str.strip()
tags_str = tags_str.replace(',', ';')
tags_list = tags_str.split(';')
for t in tags_list:
t = t.strip()
tag, is_tag_created = Tag.objects.get_or_create(name=t)
if is_tag_created:
tag.slug = slugify(t, allow_unicode=True)
tag.save()
self.object.tags.add(tag)
return response
else:
return redirect('/blog/')
self.request.POST.get('tags_str')
tags_str이름을 가진 post 데이터를 가져온다.
tags_str = tags_str.strip()
strip 함수는 parameter(default = whitespace)를 문자열의 양 끝에서 제거한다.
tag, is_tag_created = Tag.objects.get_or_create(name=t)
get_or_create함수는 두가지 값을 동시에 리턴한다. 첫 번째는 Tag 모델의 인스턴스이고,
두 번째는 이 인스턴스가 새로 생성되었는지를 나타내는 bool 형태의 값이다.
tag.slug = slugify(t, allow_unicode=True)
slug는 사람이 읽기 편한 텍스트로 고유 url을 생성하고 싶을 때 사용하는 필드이다.
관리자페이지에서는 slug를 자동으로 채워넣어 생성해주지만,
이번에는 get_or_create 함수로 생성하였기 때문에 slug를 직접 저장해야한다.
slugify는 관리자 페이지에서 slug를 생성하는 것과 같은 원리로 동작한다.
self.objects.tags.add(tag)
태그를 새로 생성했든 가져왔든, 새로 만든 Post 객체의 tags 필드에 추가해주어야 하므로
add()
를 통해 추가해준다.