[Django/DB] 댓글 기능 구현하기 (3) : Comment - User (N:1) 관계 설정

문지은·2023년 5월 9일
1

Django + Database

목록 보기
3/12
post-thumbnail

⭐️ 모델 관계 설정

Comment와 User 간 모델 관계 설정

  • 0개 이상의 댓글은 1개의 회원에 의해 작성될 수 있음
  • Comment(N) - User(1) 관계 설정
  • Comment 모델에 User 모델을 참조하는 외래 키 작성
# articles/models.py

from django.db import models
from django.conf import settings

class Comment(models.Model):
    article = models.ForeignKey(Article, on_delete=models.CASCADE)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    content = models.CharField(max_length=200)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.content

Migration 진행

migrations 파일 생성하기

$ python manage.py makemigrations
  • 이전에 User와 Article 모델 관계 설정 때와 마찬가지로 기존에 존재하던 테이블에 새로운 컬럼이 추가되어야 하는 상황이기 때문에 migrations 파일이 곧바로 만들어지지 않고 일련의 과정이 필요
  1. 첫번째 화면
  • 기본적으로 모든 컬럼은 NOT NULL 제약조건이 있기 때문에 데이터가 없이는 새로 추가되는 외래 키 필드 user_id가 생성되지 않음
    • 그래서 기본 값을 어떻게 작성할 것인지 선택해야 함
    • 1을 입력하고 Enter 진행 (다음 화면에서 직접 기본 값 입력)
  1. 두번째 화면
  • article의 user_id에 어떤 데이터를 넣을 것인지 직접 입력해야 함
    • 마찬가지로 1 입력하고 Enter 진행
    • 그러면 기존에 작성된 게시글이 있다면 모두 1번 회원이 작성한 것으로 처리됨

migrations 파일 생성 후 migrate 진행

$ python manage.py migrate

comment 테이블 스키마 변경 및 확인

⭐️ CREATE

인증된 회원의 댓글 작성 구현하기

CommentForm

  • Comment Form 출력을 확인해보면 create 템플릿에서 불필요한 필드(user)가 출력됨
  • user 필드에 작성해야 하는 user 객체는 view 함수의 request 객체를 활용해야 함

CommentForm의 출력 필드 수정

# articles/forms.py

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        exclude = ('article', 'user',)
  • 수정 확인 후 댓글 작성

외래 키 데이터 누락

  • 댓글 작성 시 NOT NULL constraint failed: articles_comment.user_id 에러 발생
    - 댓글 작성 시 외래 키에 저장되어야 할 작성자 정보가 누락되었기 때문
  • 댓글 작성 시 작성자 정보가 함께 저장될 수 있도록 save의 commit 옵션 활용
# articles/views.py

def comments_create(request, pk):
    article = Article.objects.get(pk=pk)
    comment_form = CommentForm(request.POST)
    if comment_form.is_valid():
        comment = comment_form.save(commit=False)
        comment.article = article
        comment.user = request.user
        comment.save()
    return redirect('articles:detail', article.pk)
  • 수정 후 댓글이 잘 작성되는 것을 확인할 수 있음

⭐️ READ

댓글 작성자 출력

  • detail 템플릿에서 각 댓글의 작성자 출력
<!-- articles/detail.html -->

<ul>
{% for comment in comments %}
    <li>
    {{ comment.user }} - {{comment.content}}
    <form action="{% url 'articles:comments_delete' article.pk comment.pk%}" method="POST">
        {% csrf_token %}
        <input type="submit" value="삭제">
    </form>
    </li>
{% empty %}
    <li>댓글이 없습니다 :(</li>
{% endfor %}
</ul>
  • 출력 확인

⭐️ DELETE

댓글 삭제 시 작성자 확인

  • 이제 댓글에는 작성자 정보가 함께 들어있기 때문에 현재 삭제를 요청하려는 사람과 댓글을 작성한 사람을 비교하여 본인의 댓글만 삭제할 수 있도록 함
# articles/views.py

def comments_delete(request, pk, comment_pk):
    comment = Comment.objects.get(pk=comment_pk)
    if request.user == comment.user:
        comment.delete()
    return redirect('articles:detail', pk)
  • 추가로 해당 댓글의 작성자가 아니라면, 삭제 버튼을 출력하지 않도록 함
# articles/views.py

def comments_delete(request, pk, comment_pk):
    comment = Comment.objects.get(pk=comment_pk)
    if request.user == comment.user:
        comment.delete()
    return redirect('articles:detail', pk)
  • 다른 계정으로 접속하여 다른 회원이 작성한 댓글 확인

⭐️ 인증된 사용자에 대한 접근 제한 하기

is_authenticated 와 View decorator를 활용하여 코드 정리하기

인증된 사용자인 경우만 댓글 작성 및 삭제하기

# articles/views.py
from django.views.decorators.http import require_POST

@require_POST
def comments_create(request, pk):
    if request.user.is_authenticated:
        article = Article.objects.get(pk=pk)
        comment_form = CommentForm(request.POST)
        if comment_form.is_valid():
            comment = comment_form.save(commit=False)
            comment.article = article
            comment.user = request.user
            comment.save()
        return redirect('articles:detail', article.pk)
    return redirect('accounts:login')

@require_POST
def comments_delete(request, pk, comment_pk):
    if request.user.is_authenticated:
        comment = Comment.objects.get(pk=comment_pk)
        if request.user == comment.user:
            comment.delete()
    return redirect('articles:detail', pk)

비인증 사용자는 CommentForm을 볼 수 없게 하기

{% if request.user.is_authenticated %}
	<form action="{% url 'articles:comments_create' article.pk %}" method="POST">
	  {% csrf_token %}
	  {{comment_form}}
	  <input type="submit" value="저장">
	</form>
{% else %}
	<a href="{% url 'accounts:login' %}">[댓글을 작성하려면 로그인하세요.]</a>
{% endif %}

📍 프로젝트 전체 코드 확인

profile
코드로 꿈을 펼치는 개발자의 이야기, 노력과 열정이 가득한 곳 🌈

0개의 댓글