본문 바로가기
source-code/Django

Blogs App _ 댓글 기능

by mattew4483 2021. 1. 2.
728x90
반응형

아무런 소통 없이 인기 블로그가 되기란 불가능하다.

따라서 각 게시글에 댓글을 달 수 있는 기능을 구현할 예정!


1. model 생성

댓글을 담당할 모델을 새로 하나 만들어주자!

models.py

ForeignKey를 통해 Post 모델을 상호 참조하도록!

on_delete=models.CASCADE 속성을 줘서 게시글이 지워지면 댓글도 지워지도록!

했던 내용이라 신난다!

 

2. Form 생성

Form이라는 녀석이 등장했다!

쉽게 말해 이용자가 입력할 수 있는 틀을 만든다고 생각하면 된다(아주 쉽게 말해).

 

Form을 이용하기 위해 blogs앱 내에 forms.py 라는 파일 생성!

고정된 위치가 있는 건 아니지만... blogs앱에서 사용할 Form이므로 여기 만들어두자.

 

forms.py

django에서 forms를 import!

해당 form이 참조할 모델, 즉 Comment도 불러와주자.

 

그 밑에는 이렇게 적어주면 되는데... 요상한 녀석이 등장했다. 차근차근 알아보자.

 

우선 CommentForm이란 class를 만들어 정의해준다.

이때 이 class 인자는 2개가 있는데 → 모델에 기반한 입력 틀 or 임의의 입력 틀

전자는 forms.ModelForm , 후자는 forms.Form 으로 적어주면 된다.

 

다음은 class Meta 를 통한 내부 클래스 지정.

우선 해당 form이 참조해야 할 모델을 가르쳐주자.

fields 속성을 통해 해당 모델 중 어디를 입력받을 공간을 만드는 것인지 알려주면 된다. 

즉 현재는 Comment 모델의 body 부분의 입력 틀(즉, 댓글이다!)을 만든 것!

 

이때 정말 많이 하는 실수들이 있다.

ModelForm에서 M과 F를 소문자로 쓴다던가, fields에 s를 붙이지 않는다는 가, model = ' ' 형태로 적는다던가.

에러가 뜨면 이 부분을 가장 먼저 체크하자.

 

3. Views 수정 & Templates 수정

이렇게 만든 틀(ContentForm)을 템플릿에 띄워줘야 한다!

우선 댓글 작성은 해당 게시글의 디테일 페이지에서 이뤄진다.

따라서 post_detail.html 을 띄워주는 view, 즉 PostDV에서 ContentForm을 함께 보내주면 된다.

이후 post_detail.html 에서 ContentForm을 받아 페이지 상에 띄워주자.

 

view.py

제일 먼저 Comment 모델과 CommentForm을 import 해주자.

 

post_detail.hrml을 띄워주는 PostDV에서 get_context_data를 통해 context 변수를 넘겨준다!

context 변수 안에는 우리가 만든 CommentForm이라는 틀을 담고, 이를 commentform이라고 이름 붙여줬다.

이때 CommentForm 뒤에 () 를 붙이지 않으면...

'method' object does not support item assignment 에러와 만날 수 있다.

 

detail.html

태그 위에 댓글창을 만들 예정.

따라서 해당 위치에 commentform을 띄워준다!

이렇게 작성을 마치면...!

/blogs/post/글5의 Slug

쨔잔. 일일이 만들지 않았는데도 댓글을 적을 수 있는 틀이 만들어졌다.

 

여기서 문제. 댓글을 적을 수는 있는데.... 저장할 수가 없다!

말짱 도루묵. 작성한 댓글을 제출, 저장하고 html상에도 띄워주는 기능을 추가해야 한다.

 

4. 댓글 저장

1) urls.py 작성

※ 기존 re_path(r'^post/(?P<slug>[-\w]+)/$', PostDV.as_view(), name='post_detail'), 대신

path('post/<str:slug>', PostDV.as_view(), name='post_detail'), 를 썼다!

 

?P<slug>[-\w]+)/$ ← 요 녀석은 문자열인 Slug를 url요청에 보내주기 위해 쓴 것이니,

그냥 str:slug로 문자열 인자를 넘겨주면 되는 일이었다! 

 

댓글을 저장하는 함수 create_comment에 대한 url 요청을 작성!

그런데 url 요청에서 int:post_id 와 str:slug 두 개의 값을 받는다. 왜?

이는 나중에 밝혀진다.

 

2) detail.html 수정

post_detail.html

템플릿에서 url을 작성할 때 정말 고생했던 에러가 있다.

create_comment를 작동시키는 url을 만들기 위해 {% url 'create_comment' %} 라 적으면 이런 에러가 뜬다.

분명히 create_comment 를 url에서 name으로 지칭해줬는데, 자꾸만 유효하지 않은 view란다. 으으으.

 

원인은 간단. 그 create_comment가 어디에 있는 녀석인지 가르쳐주지 않았잖나!

즉...

이런 식으로 앞에 blogs: 를 붙여 해당 앱의 url 임을 명시해주면 된다.

 

post_detail.html

아무튼 다시 html 작성!

아까 html에 불러왔던 commentform을 form태그로 감싸준다.

POST 요청(csfr token을 잊지 말자!)을 통해 댓글을 저장하는 함수(create_comment)를 작동시킬 예정!

 

여기서 또 짚고 가야 할 부분.

첫째, 난데없이 등장한 object는 DetailView에서 보내준, Post 모델 전체를 담은 context 변수명이다.

둘째, 즉 object.id 는 각 게시글의 id를, object.slug 는 각 게시글의 slug를 뜻한다. 이를 url 요청 시 함께 보내준다.

셋째, object.comment ← 요놈은 Post 모델에 ForeinKey 관계로 연결된 Comment 모델을 뜻한다.

넷째, 특정 모델에 ForeinKey로 연결된 모델의 모든 요소를 가져오겠다 → _set.all 을 붙여주면 된다.

 

3) create_comment 함수 작성

대망의 create_comment 함수 작성.

클래스형 뷰로 적으면 참 좋겠지만, 부족한 능력. 흑흑흑.

함수형 뷰로 대체되었다.

 

댓글 작성을 위해 구글에 도움을 요청하면... 

대부분 id 값을 통해 특정 게시글에 해당된 댓글임을 찾아가도록 해뒀다.

하지만 정말 난감한 사실. 우리는 게시글을 찾아갈 때 사용할 고유할 값을 Slug 를 통해 지칭했다!

즉 단순한 각 게시글 별 번호인 id가 아닌, 게시글에 해당된 단어인 Slug 로 찾아가야 하는 것.

https://korinkorin.tistory.com/m/27 참고.

우선 해맑은 코린이의 정리로 대략적인 흐름을 살펴보자면...

url을 통해 보낸 POST 요청을 filled_form 으로.

is_valid 로 해당 요청이 유효한 값이라면 해당 요청을 저장, 그렇지 않으면 다시 detail 페이지로 redirect.

 

temp_form = filled_form(commit=False)로 저장을 잠시 미루고,

temp_form.post = Post.objects.get(id=post_id) 를 통해 요청 해당 게시글의 id 값을 가르쳐준다.

이후 temp_form.save()로 저장!

 

유효하지 않을 경우 redirect('detail', post_id) 를 통해 post_id에 해당하는 게시글의 detail 페이지로 이동하도록 했다.

 

ulrs.py

즉 이 경우 url은 이렇게,

 

post_detail.html

html에서 url 요청은 이렇게 될 테다.

 

그런데 문제...

post_detail.html 에서 댓글을 작성하면... 페이지를 찾질 못한다!!

admin페이지에서 보면 작성된 댓글은 잘 저장된 것을 볼 수 있다.

그렇다면 문제는? 댓글 저장 후 다시 해당 게시글의 detail 페이지로 redirect 해야 하는데, 이 작업이 안 되는 것.

 

사실 당연한 문제다.

view.py

우리는 이 부분을 통해 post_id 라는 인자를 넘겨주면서 detail 이라는 url을 작동시키려고 했다.

그런데 detail이라는 url은?? 인자로 <str:slug>, 즉 해당 게시글의 Slug를 받는다! id가 아니라!!!

즉 우리가 redirect를 통해 보내준 blogs/post/id값 ← 이 요청은..? 아무런 url과도 매칭 되지 않는다!

 

그럼 당최 어떻게 하면 좋을까.

views.py

간단하다. redirect 요청을 slug 인자와 함께 보내주면 된다!

 

urls.py

url에서 <str:slug> 라는 문자열도 함께 보내준다.

 

그런데 이 slug는 또 어디서 나왔는디? → 당연히 html 에서 create_comment 란 url을 작동시켰을 때 전해줘야 한다!

post_detail.html

쨔잔. create_comment라는 url을 작동했을 때, 해당 게시글의 Slug(object.slug)를 함께 보내주도록!

보내준 요놈을? views에서 받아주자.

 

views.py

이전 create_comment 함수에서는 post_id라는, 게시글 하나하나에 해당되는 id 값을 보내줬다면,

지금은 slug라는 게시글 하나하나에 해당되는 단어들을 보내줬다.

신나는 마음으로 댓글을 작성해보면..!

띠요용. id라는 Field는 숫자여야 하는데... first-post-slug(해당 글의 Slug다)란 문자열이 왔단다.

으윽 왜 숫자여야 하는겨!

참조하는 게시글이 무엇인지를 명시해주기 위해 Post.objects.get을 썼는데...!

요 get은 문자열이 아닌, 숫자가 필요하다!!!

그렇다면 방법은? 역시나 간단. 해당 게시글의 id 값 역시 함께 넘겨주면 된다!

 

당최 무슨 말인고 하니...

urls.py

아항! 이제야 urls.py를 왜 이렇게 썼는지 알 수 있다!

redirect를 위해 str:slug를, objects.get을 위해 int:post_id를 넘겨준 것이었다!

 

post_detail.html

html의 url 요청에서도 object.slug 뿐만 아니라 object.id도 보내주자!

 

views.py

마지막으로 views에서도 slug 뿐만 아니라 post_id를 받아온다!

해당 Post가 무엇인지 찾을 때(objects.get)는 post_id(숫자)를,

다시 해당 게시글의 detail 페이지로 이동할 때(redirect)는 Slug(문자)를 사용한 것이다! 와우!


오늘도 타닥타닥 잘 쳤다면!

post_detail.html

이렇게 detail 페이지에서 댓글 작성이 가능하다.

작성 버튼을 누르면...!

 

쨔잔. 이렇게 밑에 작성한 댓글이 뜬다! 

728x90
반응형

'source-code > Django' 카테고리의 다른 글

Blogs App _ 인증 기능  (0) 2021.01.05
Blogs App _ 검색 기능  (0) 2021.01.04
Blogs App _ tag 기능  (0) 2021.01.01
Blogs App  (0) 2020.12.31
Books App _ log 만들기  (0) 2020.12.30