ForeignKey(to, on_delete)class Comment(models.Model):
article = models.ForeignKey(Article, 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)만들어지는 필드 이름 규칙: ‘작성한 외래 키 필드명’ + ‘_id’
댓글 테이블의 article_id 외래 키 필드 확인: bigint 자료형 확인
⇒ 참조하는 클래스 이름의 소문자(단수형)로 작성하는 것이 권장되었던 이유
연습을 위한 shell 환경에서 진행
ipython 설치 필요 ($pip install ipython)
django shell 시작 후 댓글 작성을 위한 게시글 하나 작성
# django shell 실행
python manage.py shell
# 게시글 생성
Article.objects.create(title='title', content='content')
댓글 생성 후 바로 저장 확인
# Comment 클래스의 인스턴스 comment 생성
comment = Comment()
# 인스턴스 변수 저장
comment.content = 'first comment'
# DB에 댓글 저장
comment.save()
# 에러 발생
IntegrityError: NOT NULL constraint failed: articles_comment.article_id
=> articles_comment 테이블의 ForeignKeyField, article_id 값이 저장 시 누락되었기 때문
게시글 정보를 가져와 댓글 저장
# 게시글 정보 조회하여 가져오기
article = Article.objects.get(pk=1)
# 외래 키 데이터 입력
comment.article = article
# 직접적으로 테이블의 필드에 pk를 넣어도 저장되지만 권장하지 않음
# comment.article_id = article.pk
# 댓글 저장 및 확인
comment.save()
comment 인스턴스를 통한 article 값 참조하기
comment.pk
# 출력: 1
comment.content
# 출력: 'first comment'
# 클래스 변수명인 article로 조회 시 해당 참조하는 게시물 객체를 조회할 수 있음
comment.article
# 출력: <Article: Article object (1)>
# 테이블 상의 article_id 값은 해당 게시글의 Primary Key(PK)값만 확인가능
comment.article_id
# 출력: 1
comment를 통한 article 객체의 데이터 참조하기
# 1번 댓글이 작성된 게시글의 pk 조회
comment.article.pk
# 출력: 1
# 1번 댓글이 작성된 게시글의 content 조회
comment.article.content
# 출력: 'content'
두 번째 댓글 생성 및 데이터 확인
commentary = Comment(content='second comment', article=article)
commentary.save()
commentary.pk
# 출력 2
commentary
# 출력: <Comment: Comment object (2)>
Commentary.article.pk
# 출력: 1
.all() 사용하기 → X# 특정 게시물의 댓글이 아닌 모든 댓글을 조회함
comment = Comment.objects.all().filter() 사용하기# 특정 게시글 정보를 가져온 후 filter를 이용할 수 있음
article = Article.objects.get(pk=1)
comments = Comment.objects.filter(article=article)article.comment_set.all()article: 모델 인스턴스comment_set: related manager (역참조 이름)all: QuerySet APIcomment.articlearticle.comment_set.all()python manage.py shellarticle = Article.objects.get(pk=1)article.comment_set.all()
<QuerySet [<Comment: Comment object (1)>, <Comment: Comment object (2)>]># 1번 게시글의 모든 댓글 정보들을 저장
comments = article.comment_set.all()
# 1번 게시글 댓글 정보들을 반복하여 개별 댓글 내용 출력
for comment in comments:
print(comment.content)# articles/forms.py
from .models import Article, Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = '__all__'def detail(request, pk):
article = Article.objects.get(pk=pk)
comment_form = CommentForm()
context = {
'article': article,
'comment_form': comment_form,
}
return render(request, 'articles/detail.html', context)<!-- 게시글 정보 출력 코드 -->
<hr>
<form action="#" method="POST">
{% csrf_token %}
{{ comment_form }}
<input type="submit">
</form>
from .models import Article, Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('content', )path('<int:pk>/', views.detail, name='detail')app_name = 'article'
urlpatterns = [
...
path('<int:pk>/comments/', views.comments_create, name='comments_create'),
]<form action="{% url 'articles:comments_create' article.pk %}" method="POST">
{% csrf_token %}
{{ comment_form }}
<input type="submit">
</form>def comments_create(request, pk):
article = Article.objects.get(pk=pk)
comment_form = CommentForm(request.POST)
if comment_form.is_valid():
comment_form.save()
return redirect('articles:detail', article.pk)
# 유효성 검사에 실패 했을 때 에러 정보 및 필요 데이터 전달
context = {
'article': article,
'comment_form': comment_form,
}
return render(request, 'articles/detail.html', context)save(commit=False)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.save() # 댓글 정보 DB에 저장 요청
return redirect('articles:detail', article.pk)
# 유효성 검사에 실패 했을 때 에러 정보 및 필요 데이터 전달
context = {
'article': article,
'comment_form': comment_form,
}
return render(request, 'articles/detail.html', context)def detail(request, pk):
article = Article.objects.get(pk=pk)
comment_form = CommentForm()
# 특정 게시글의 댓글을 역참조로 조회
comments = article.comment_set.all()
context = {
'article': article,
'comment_form': comment_form,
'comments': comments,
}
return render(request, 'articles/detail.html', context)<a href="#">UPDATE</a>
<h4>댓글 목록</h4>
<ul>
{% for comment in comments %}
<li>{{ comment.content }}</li>
{% endfor %}
</ul> app_name = 'articles'
urlpatterns = [
...,
path('<int:article_pk>/comments/<int:comment_pk>/delete/', views.comments_delete, name='comments_delete'),
]def comments_delete(request, article_pk, comment_pk):
comment = Comment.objects.get(pk=comment_pk)
comment.delete()
# 댓글 삭제 후 article 게시글 상세 페이지로 이동
return redirect('articles:detail', article_pk)<a href="#">UPDATE</a>
<h4>댓글 목록</h4>
<ul>
{% for comment in comments %}
<li>
{{ comment.content }}
<form action="{% url 'articles:comments_delete' article.pk comment.pk %}" method="POST">
{% crsf_token %}
<input type="submit" value="DELETE">
</form>
</li>
{% endfor %}
</ul>
from .models import Article, Comment
admin.site.register(Article)
admin.site.register(Comment) {% for comment in comments %}
<li>
{{ comment.content }}
<form action="{% url 'articles:comments_delete' article.pk comment.pk %}" method="POST">
{% crsf_token %}
<input type="submit" value="DELETE">
</form>
</li>
{% empty %}
<p>댓글이 없어요..</p>
{% endfor %}length 활용<!-- views.py에서 전달받은 댓글로 길이 확인-->
{{ comments|length }}
<!--역참조를 이용하여 댓글 길이 확인-->
{{ article.comment_set.all|length }}count() 메서드 활용<!-- 역참조로 QuerySet API 이용 댓글 길이 확인 -->
{{ article.comment_set.count }}