[Django] 댓글 기능 구현
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 옵션
- 어떠한 모델에 속하는 필드인지 기록하는 것이다.
- 참조하는 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를 사용하여 댓글이 없음을 표시한다.