🧩

015 댓글

 
댓글 모델을 생성하고 admin에 표시해보도록 하겠습니다.
 
Django를 하시고 나면 다른 프레임워크의 구조가 훨씬 더 잘 보이게 됩니다. 대부분 MVC 패턴을 이용하기 때문에 DB 통신하는 부분, 화면에 표시하는 부분, 화면을 컨트롤 하는 부분으로 나누어져 있습니다. 세세한 문법들이 다를 뿐 훨씬 더 쉽게 접근하실 수 있습니다.
 
post 앱 안에 있는 models.pyadmin.py를 수정해 보도록 하겠습니다. 맨 아래에 Comment 모델을 만들어 보도록 하겠습니다.
 
파일명 : post/models.py
... class Comment(models.Model): post = models.ForeignKey(Post, on_delete=models.CASCADE) author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) content = models.CharField(max_length=40) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ['-id'] def __str__(self): return self.content
 
모델을 적용해 보도록 하겠습니다.
(venv)root@goorm:/workspace/instaclone/instaclone# python3 manage.py makemigrations
 
notion imagenotion image
 
문제가 없이 되는 것을 확인하시고 create model comment가 생성 된 것을 알 수 있습니다.
(venv)root@goorm:/workspace/instaclone/instaclone# python3 manage.py migrate
 
notion imagenotion image
 
이제 admin.py를 수정하겠습니다. Comment 모델을 import 하겠습니다.
 
파일명 : post/admin.py
from .models import Post, Like, Bookmark, Comment
 
다음 소스를 맨 아래에 추가하도록 하겠습니다.
 
파일명 : post/admin.py
... @admin.register(Comment) class CommentAdmin(admin.ModelAdmin): list_display = ['post', 'content', 'author', 'created_at'] list_display_links = ['post', 'content', 'author']
 
Comment가 추가가 됬으니 CommentInline 클래스도 추가하겠습니다.
 
파일명 : post/admin.py
... class CommentInline(admin.TabularInline): model = Comment
 
PostAdmin 클래스 부분에서 inlinesCommentInline를 추가하도록 하겠습니다.
 
파일명 : post/admin.py
@admin.register(Post) class PostAdmin(admin.ModelAdmin): ... inlines = [LikeInline, CommentInline] ...
 
admin에 다시 한번 접속하도록 하겠습니다. 새로고침을 하고 Comments 부분이 추가된 것을 볼 수 있습니다.
notion imagenotion image
 
comment를 추가해 보도록 하겠습니다. 입력값을 입력하고 저장 버튼을 누르시면 comment가 추가된 것을 확인하실 수 있습니다.
notion imagenotion image
notion imagenotion image
 
이번에는 실제 페이지에서 댓글들을 DB에 저장된 실제 댓글로 변경하겠습니다. comment의 내용을 서버에서 불러오도록 하겠습니다. post_list.html 파일로 가셔서 comment 부분을 확인하도록 하겠습니다.
 
div.comment 태그의 id 값의 번호 부분을 comment의 실제 id(또는 pk)로 변경을 하도록 하겠습니다.
 
파일명 : post/templates/post/post_list.html
<div class="comment" id="comment-list-ajax-post{{post.id}}">
 
div.comment-detail 태그를 {% for %} 태그로 감쌉니다.
 
파일명 : post/templates/post/post_list.html
{% for comment in post.comment_set.all %} <div class="comment-detail"> <div class="nick_name m_text">dongdong2</div> <div>강아지가 너무 귀여워요~!</div> </div> {% endfor %}
 
div.comment-detail 태그 마다 "comment{{ comment.id }}" 형태로 id 값을 주도록 하겠습니다.
 
파일명 : post/templates/post/post_list.html
{% for comment in post.comment_set.all %} <div class="comment-detail" id="comment{{ comment.id }}"> <div class="nick_name m_text">dongdong2</div> <div>강아지가 너무 귀여워요~!</div> </div> {% endfor %}
 
닉네임 부분을 comment를 작성한 사람으로 변경해주도록 하겠습니다. 또 내용을 실제 comment의 내용으로 바꿉니다.
 
파일명 : post/templates/post/post_list.html
{% for comment in post.comment_set.all %} <div class="comment-detail" id="comment{{ comment.id }}"> <div class="nick_name m_text">{{ comment.author.profile.nickname}}</div> <div>{{ comment.comment }}</div> </div> {% endfor %}
 
댓글의 주인이 현재 로그인한 유저라면 댓글 삭제 버튼을 노출시킵니다.
 
파일명 : post/templates/post/post_list.html
{% for comment in post.comment_set.all %} <div class="comment-detail" id="comment{{ comment.id }}"> <div class="nick_name m_text">{{ comment.author.profile.nickname}}</div> <div>{{comment.content}}</div> {% if user == comment.author %} <input type="button" class="del-comment" data-name="comment_delete" value="삭제" name="{{ comment.id }}"> {% endif %} </div> {% endfor %}
 
출력결과를 확인해 보도록 하겠습니다.
notion imagenotion image
 
Ajax를 이용해 댓글달고 게시할 수 있도록 urls.py, views.py, forms.py를 수정하도록 하겠습니다.
 
urls.py 에서 comment_newcomment_delete 두 가지 url을 생성하도록 하겠습니다.
 
파일명 : post/urls.py
urlpatterns = [ ... path('comment/new/', comment_new, name='comment_new'), path('comment/delete/', comment_delete, name='comment_delete'), ]
 
views.py 에서 comment_new 함수와 comment_delete 함수를 정의 하도록 하겠습니다. .models 에서 Comment 모델을 import 합니다.
 
파일명 : post/views.py
from .models import Post, Comment
 
comment 작성하는 것도 로그인이 되어야 합니다.
 
파일명 : post/views.py
@login_required def comment_new(request): pk = request.POST.get('pk') # Ajax를 통신하는 부분 post = get_object_or_404(Post, pk=pk) if request.method == 'POST': form = CommentForm(request.POST) if form.is_valid(): comment = form.save(commit=False) comment.author = request.user comment.post = post comment.save() return render(request, 'post/comment_new_ajax.html', { 'comment': comment, }) return redirect("post:post_list") @login_required def comment_new_detail(request): pk = request.POST.get('pk') post = get_object_or_404(Post, pk=pk) if request.method == 'POST': form = CommentForm(request.POST) if form.is_valid(): comment = form.save(commit=False) comment.author = request.user comment.post = post comment.save() return render(request, 'post/comment_new_detail_ajax.html', { 'comment': comment, })
 
여기서 comment_new_detail 함수는 뒤의 상세 페이지에서 댓글을 추가할 때 사용할 함수입니다. comment_new와 비슷하므로 미리 만들어 놓겠습니다.
 
이번에는 CommentFrom을 작성하도록 하겠습니다. forms.py로 들어가셔서 .models에서 Comment를 추가하도록 하겠습니다.
 
파일명 : post/forms.py
from .models import Post, Comment
 
PostForm 아래에 작성하도록 하겠습니다.
 
파일명 : post/forms.py
class CommentForm(forms.ModelForm): content = forms.CharField(label='', widget=forms.TextInput(attrs={ 'class': 'comment-form', 'size': '70px', 'placeholder': '댓글 달기...', 'maxlength': '40', })) class Meta: model = Comment fields = ['content']
 
post/views.pypost_list함수에서 CommentFormpost_list.html로 전달할 수 있도록 수정합니다.
 
파일명 : post/views.py
from .forms import CommentForm def post_list(request, tag=None): ... comment_form = CommentForm() if request.user.is_authenticated: ... return render(request, 'post/post_list.html', { ... 'comment_form': comment_form, }) else: return render(request, 'post/post_list.html', { ... 'comment_form': comment_form, })
 
script_ajax.html에서 다음 부분을 추가합니다.
 
파일명 : post/templates/post/script_ajax.html
<script type="text/javascript"> (function(){ const delegation = document.querySelector('.contents_box'); function delegationFunc(e){ ... if (elem.matches('[data-name="heartbeat"]')){ ... }else if (elem.matches('[data-name="bookmark"]')){ ... }else if (elem.matches('[data-name="comment"]')){ console.log('새댓글') var pk = elem.getAttribute('name'); var content = document.querySelector('#add-comment-post'+pk+'>input[type=text]').value; console.log(content); if(content.length > 140) { alert("댓글은 최대 140자 입력 가능합니다. 현재 글자수 :"+content.length); return; } if(content.length == 0) { alert("글자를 하나라도 넣어주세요. 현재 글자수 :"+content.length); return; } $.ajax({ type: "POST", url: "{% url 'post:comment_new' %}", data: { 'pk': pk, 'content': content, 'csrfmiddlewaretoken': '{{ csrf_token }}', }, dataType: "html", success: function(data, textStatus, jqXHR){ console.log("성공!") document.querySelector("#comment-list-ajax-post"+pk).insertAdjacentHTML("afterbegin", data); }, error: function(request, status, error){ alert("code:"+request.status+"\n"+"message:"+request.responseText+"\n"+"error:"+error); }, }); }else if(elem.matches('[data-name="comment_delete"]')){ var pk = elem.getAttribute('name'); $.ajax({ type: "POST", url: "{% url 'post:comment_delete' %}", data: { 'pk': pk, 'csrfmiddlewaretoken': '{{ csrf_token }}', }, dataType: "json", success: function(response){ if(response.status){ document.querySelector('#comment'+pk).remove(); } }, error: function(request, status, error){ alert("code:"+request.status+"\n"+"message:"+request.responseText+"\n"+"error:"+error); } }); } elem.classList.toggle('on'); } delegation.addEventListener('click',delegationFunc); }) </script>
 
post_list.html에서 댓글 입력하는 창을 수정하겠습니다. div.comment_fieldid 값을 수정합니다.
 
파일명 : post/templates/post/script_ajax.html
<div class="comment_field" id="add-comment-post{{ post.id }}">
 
이 글에 맞는 pk 값이 들어가게 됩니다. 확인해 보면 숫자가 들어가 있는 것을 확인 할 수 있습니다. if 태그를 사용하여 로그인 여부를 확인합니다.
 
파일명 : post/templates/post/script_ajax.html
<div class="comment_field" id="add-comment-post{{ post.id }}"> {% if user.is_authenticated %} {{ comment_form }} <div class="upload_btn m_text" name="{{post.id}}" data-name="comment">게시</div> {% else %} {{ comment_form }} <!--게시가 안되고 에러창이 뜨게 됩니다.--> <div class="upload_btn m_text" name="{{post.id}}" data-name="comment" onclick="alert('댓글을 작성하려면 로그인이 필요합니다')">게시</div> {% endif%} </div>
 
로그인을 하지 않았다면 '게시' 버튼을 눌렀을 시 '댓글을 작성하려면 로그인이 필요합니다' 메시지가 뜹니다. 접속하여 댓글 작성 창이 잘 뜨는지 확인합니다.
notion imagenotion image
 
아직 댓글 게시는 작동하지 않습니다. 아직 comment_new_ajax.html 파일을 만들지 않았기 때문입니다.
views.pycomment_new 함수에서 만들어진 새로운 댓글을 이용해 comment_new_ajax.html 템플릿으로 댓글 태그를 생산한 다음 post_list.html에 끼워 넣어주는 원리입니다.
 
comment_new_ajax.html 파일을 만들도록 하겠습니다.
 
파일명 : post/templates/post/comment_new_ajax.html
<div class="comment-detail" id="comment{{ comment.id }}"> <div class="nick_name m_text">{{ comment.author.profile.nickname }}</div> <div>{{ comment.content }}</div> {% if user == comment.author %} <input type="button" class="del-comment" data-name="comment_delete" value="삭제" name="{{ comment.id }}"> {% endif %} </div>
 
이제 게시 버튼을 누르면 바로 댓글이 달리는 것을 확인할 수 있습니다.
notion imagenotion image
 
그리고 나서 삭제 버튼을 누르시면 comment가 삭제되는 것을 볼 수 있습니다.
notion imagenotion image