아무런 소통 없이 인기 블로그가 되기란 불가능하다.
따라서 각 게시글에 댓글을 달 수 있는 기능을 구현할 예정!
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을 띄워준다!
이렇게 작성을 마치면...!
쨔잔. 일일이 만들지 않았는데도 댓글을 적을 수 있는 틀이 만들어졌다.
여기서 문제. 댓글을 적을 수는 있는데.... 저장할 수가 없다!
말짱 도루묵. 작성한 댓글을 제출, 저장하고 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 임을 명시해주면 된다.
아무튼 다시 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 로 찾아가야 하는 것.
우선 해맑은 코린이의 정리로 대략적인 흐름을 살펴보자면...
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 페이지로 이동하도록 했다.
즉 이 경우 url은 이렇게,
html에서 url 요청은 이렇게 될 테다.
그런데 문제...
post_detail.html 에서 댓글을 작성하면... 페이지를 찾질 못한다!!
admin페이지에서 보면 작성된 댓글은 잘 저장된 것을 볼 수 있다.
그렇다면 문제는? 댓글 저장 후 다시 해당 게시글의 detail 페이지로 redirect 해야 하는데, 이 작업이 안 되는 것.
사실 당연한 문제다.
우리는 이 부분을 통해 post_id 라는 인자를 넘겨주면서 detail 이라는 url을 작동시키려고 했다.
그런데 detail이라는 url은?? 인자로 <str:slug>, 즉 해당 게시글의 Slug를 받는다! id가 아니라!!!
즉 우리가 redirect를 통해 보내준 blogs/post/id값 ← 이 요청은..? 아무런 url과도 매칭 되지 않는다!
그럼 당최 어떻게 하면 좋을까.
간단하다. redirect 요청을 slug 인자와 함께 보내주면 된다!
url에서 <str:slug> 라는 문자열도 함께 보내준다.
그런데 이 slug는 또 어디서 나왔는디? → 당연히 html 에서 create_comment 란 url을 작동시켰을 때 전해줘야 한다!
쨔잔. create_comment라는 url을 작동했을 때, 해당 게시글의 Slug(object.slug)를 함께 보내주도록!
보내준 요놈을? views에서 받아주자.
이전 create_comment 함수에서는 post_id라는, 게시글 하나하나에 해당되는 id 값을 보내줬다면,
지금은 slug라는 게시글 하나하나에 해당되는 단어들을 보내줬다.
신나는 마음으로 댓글을 작성해보면..!
띠요용. id라는 Field는 숫자여야 하는데... first-post-slug(해당 글의 Slug다)란 문자열이 왔단다.
으윽 왜 숫자여야 하는겨!
참조하는 게시글이 무엇인지를 명시해주기 위해 Post.objects.get을 썼는데...!
요 get은 문자열이 아닌, 숫자가 필요하다!!!
그렇다면 방법은? 역시나 간단. 해당 게시글의 id 값 역시 함께 넘겨주면 된다!
당최 무슨 말인고 하니...
아항! 이제야 urls.py를 왜 이렇게 썼는지 알 수 있다!
redirect를 위해 str:slug를, objects.get을 위해 int:post_id를 넘겨준 것이었다!
html의 url 요청에서도 object.slug 뿐만 아니라 object.id도 보내주자!
마지막으로 views에서도 slug 뿐만 아니라 post_id를 받아온다!
해당 Post가 무엇인지 찾을 때(objects.get)는 post_id(숫자)를,
다시 해당 게시글의 detail 페이지로 이동할 때(redirect)는 Slug(문자)를 사용한 것이다! 와우!
오늘도 타닥타닥 잘 쳤다면!
이렇게 detail 페이지에서 댓글 작성이 가능하다.
작성 버튼을 누르면...!
쨔잔. 이렇게 밑에 작성한 댓글이 뜬다!
'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 |