Django

[Django] 댓글 기능 구현

김디니 2022. 10. 18. 20:36

one to many relationship

RDB(Relational Database)

외래키를 사용하여 서로 다른 테이블 간의 관계를 만든다.

users, role 테이블에서 외래키를 사용하여 articles 테이블을 생성한다.

1 : N 관계

  • one - to many relationships
  • 한 테이블의 0개 이상의 레코드가 다른 테이블의 레코드 한개와 관련되어 있다.

 

1 : N = Article : Comment

모델 관계 설정

  • 게시글은 댓글을 0개 이상 가진다.
  • 게시글(1)은 여러 개의 댓글(N)을 가진다.
  • 게시글은 댓글을 가지지 않을 수도 있다.
  • 댓글은 반드시 하나의 게시글(1)에 속한다.

 

Django Relationship fields

  • OneToOneField()
    • 1 : 1
  • ForeignKey()
    • 1 : N
  • ManyToManyField()
    • N : M

 

ForeignKey

  • 외래 키 속성
  • 2개의 필수 위치 인자가 필요하다.
    • 참조하는 models class, on_delete 옵션
      • 어떠한 모델에 속하는 필드인지 기록하는 것이다.

on_delete

  • 외래 키가 참조하는 객체가 사라졌을 때, 외래키를 가진 객체를 어떻게 처리할 지 정의한다.
    • 게시글이 삭제되면 댓글은 어떻게 할 것인가 정한다.
      • CASCADE를 사용해서 외래키를 가진 객체를 같이 지운다!
  • 데이터 무결성을 위해서 매우 중요한 설정이다.
    • CASCADE
    • PROTECT, SET_NULL 등등

 

구현

1. Comment model 정의

외래키 필드는 외래키 클래스를 작성하는 위치와 상관없이 필드의 마지막에 작성한다.

# articles/models.py

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

외래키 인자로 불러온 class와 on_delete, CASCADE를 적어준다!

 

1-1. Makemigration, migrate 진행

model에 대한 수정사항이 발생했기 때문에!

 

2. CommentForm 작성

# articles/forms.py
from .models import Article, Comment  

class CommentForm(forms.ModelForm):
    class Meta:
         model = Comment
         fields = ['content']

 

3. detail 페이지에서 CommentForm 출력하는 함수 정의

views.py에서 함수를 정의한다.

# articles/views.py
from .forms import ArticleForm, CommentForm   

def detail(request, pk):
    article = Article.objects.get(pk=pk)
    comment_form = CommentForm()
    context = {
         'article': article,
         'comments': article.comment_set.all(),
         'comment_form': comment_form,
    }
    return render(request, 'articles/detail.html', context)

comments 변수로 article의 comment를 comments에 넘겨준다.

..._set...은 ForeignKey의 필드값을 불러올 때 사용하는 것이다. 뒤에 all()을 붙여줌으로 comment의 모든 정보를 가져오는 것이다.

이는 article에 있는 모든 댓글을 가져와 context로 넘겨주어 detail 페이지에서 보여줄 수 있도록 한다.

comment_form 변수로 CommentForm 모델의 Form을 받아온다.

모델 Form을 받아온 변수를 context로 넘겨주며 html파일에 인식할 수 있도록 한다.

 

def comment_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.save()
    return redirect('articles:detail', article.pk)

댓글을 새로 작성할 수 있는 함수를 views.py에 추가해준다.

comment_create는 detail과 값을 받아오는 것은 동일하다.

comment_form에 요정한 POST 데이터를 CommentForm으로 받아 comment_form 변수에 할당한다.

if문으로 유효성 검사를 해주는데 이때 데이터베이스에 자동으로 저장하지 않도록 commit=False를 해준다.

이는 저장 전 (save()) 잠깐 멈춰주는 역할을 해준다.

잠깐 멈춰주고, article 객체에 새로 달아준 댓글을 저장해준다.

 

4. detail 페이지에서 CommentForm 출력

detail.html에서 Form을 출력할 수 있도록 한다.

<!-- articles/detail.html -->

{% extends 'base.html' %}
{% load django_bootstrap5 %}

{% block content %}
  ...
  <a href="{% url 'articles:index' %}">back</a>
  <hr>
  <form action="#" method="POST">
    {% csrf_token %}
    {% bootstrap_form comment_form %}
    {% bootstrap_button button_type="submit" content="OK" %}
  </form>

  {% for comment in comments %}
    <p>{{ comment.content }}</p>
    <hr>    
  {% empty %}
    <p>댓글이 없어요 ㅠ_ㅠ</p>
  {% endfor %}

{% endblock content %}

form 태그를 통해 POST로 받아온 데이터 중 context로 넘겨 받은 comment_form을 출력하도록 한다.

즉, 댓글을 쓸 수 있는 Form을 불러오는 것이다!

하단의 for문을 통해 detail 페이지에 댓글을 모두 보여준다.

 

5. url 설정

path('<int:pk>/comments/', views.comment_create, name='comment_create'),

 

댓글 삭제 기능 구현

1. url 설정

# articles/urls.py
urlpatterns = [
...,
path('<int:article_pk>/comments/<int:comment_pk>/delete/', views.comments_delete, name='comments_delete'),
]

urls.py에서 comments_delete의 url을 설정해준다.

2. views.py 설정

# articles/views.py
def comments_delete(request, article_pk, comment_pk):
     comment = Comment.objects.get(pk=comment_pk)
     comment.delete()
return redirect('articles:detail', article_pk)

Comment.objects.get(pk=comment_pk).delete() 로 설정 가능하다!

3. detail.py 설정

<!-- articles/detail.html -->
{% block content %}
...
<h4>댓글 목록</h4>
<ul>
   {% for comment in comments %}
    <li>
     {{ comment.content }}
      <form action="{% url 'articles:comments_delete' article.pk comment.pk %}" method="POST">
      {% csrf_token %}
      <input type="submit" value="DELETE">
      </form>
    </li>
   {% endfor %}
</ul>
<hr>
...
{% endblock content %}

form 태그로 해당 article의pk와 comment의 pk를 지정하여 comments_delete 함수를 불러와 삭제하도록 한다.

 

댓글 개수 출력

lenfth 사용

{{ comments|length }}
{{ article.comment_set.all|length }}

count() 사용

{{ comments.count }}
{{ article.comment_set.count }}
<!-- articles/detail.html -->

<h4>댓글 목록</h4>
{% if comments %}
    <p><b>{{ comments|length }}개의 댓글이 있습니다.</b></p>
{% endif %}

detail.html에 length를 사용하여 댓글 개수를 출력한다.

댓글이 없는 경우

<!-- articles/detail.html -->

...
{% empty %}
<p>댓글이 없어요..</p>

detail.html에 empty를 사용하여 댓글이 없음을 표시한다.