0. 서론
지난번에 이어서 이제는 게시글 댓글 기능을 구현해보고 마이페이지를 통해서 본인의 작성글과 작성댓글을 알아보고 또한 이미지와 닉네임 변경 기능을 넣어볼 생각입니다.
1. 댓글 기능 구현
1-1) models.py
댓글에 관한 모델을 구현하지않았기때문에 새로운 모델을 만들어야합니다.
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.SET_NULL, related_name='comments', null=True) # 게시글과의 관계 설정
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True) # nullable 설정
author_nickname = models.CharField(max_length=50, blank=True) # 작성자의 닉네임을 저장하는 필드
content = models.TextField('댓글 내용')
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.author_nickname}의 댓글"
def save(self, *args, **kwargs):
if self.author and not self.author_nickname: # 닉네임이 비어있을 경우에만 설정
self.author_nickname = self.author.nickname
super().save(*args, **kwargs)
# 이렇게 하고 makemigrations와 migrate를 진행해주었습니다.
1-2) views.py
기존 게시글 상세페이지에서 댓글 작성기능을 넣었습니다.
class PostView(View):
def get(self, request, pk=None):
if pk:
# 게시글 상세 조회
post = get_object_or_404(Post, pk=pk)
comments = post.comments.all() # 해당 게시글의 모든 댓글
form = CommentForm()
return render(request, 'boards/post_detail.html', {'post': post, 'comments': comments, 'form': form})
else:
# 게시글 목록 조회
posts = Post.objects.all().order_by('-created_at')
return render(request, 'boards/post_list.html', {'posts': posts})
# 댓글 작성
def post(self, request, pk):
post = get_object_or_404(Post, pk=pk)
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.author = request.user # 현재 로그인한 사용자를 작성자로 설정
comment.post = post # 댓글이 달린 게시글 설정
comment.save()
# 포인트 추가
request.user.point += 1
request.user.save()
return redirect('post-detail', pk=post.pk)
comments = post.comments.all()
return render(request, 'boards/post_detail.html', {'post': post, 'comments': comments, 'form': form})
1) 주어진 pk값에 해당하는 게시글을 데이터베이스에서 가져옵니다. 없다면 404오류를 반환하고 이를 변수에 저장합니다.
2) CommentForm에 post요청 데이터를 전달해서 댓글 작성 폼을 초기화합니다.
3) 폼 데이터 유효 여부를 판별하고 현재 로그인작성자가 요청자로 설정하고 저장합니다.
4) 저장한 후에 리디렉션을 통해 다시 게시글 상세페이지로가 작성한 댓글을 확인하고 또다시 댓글작성이 가능하도록 폼을 보여줍니다.
class CommentDeleteView(View):
def post(self, request, pk):
comment = get_object_or_404(Comment, pk=pk)
# 댓글 작성자만 삭제 가능
if comment.author != request.user:
return HttpResponseForbidden("삭제 권한이 없습니다.")
comment.delete()
# 포인트 차감
request.user.point -= 1
request.user.save()
return redirect('post-detail', pk=comment.post.pk)
1) 댓글 삭제기능을 구현하였습니다.
2) 댓글 작성자와 로그인한 작성자가 같은지 여부를 확인하고 삭제를 실행하고 리디렉션하도록 하였습니다.
1-3) urls.py
from django.conf import settings
from django.conf.urls.static import static
from django.urls import path
from . import views # 전체 views 모듈을 import하여 views.로 접근
urlpatterns = [
# API 기반 뷰 URL 설정
path('api/posts/', views.ArticleView.as_view()), # 게시글 목록 조회 및 생성 (API)
path('api/posts/<int:pk>/', views.ArticleDetailView.as_view()), # 게시글 상세 조회, 수정, 삭제 (API)
# HTML 기반 뷰 URL 설정
path('', views.PostView.as_view(), name='post-list-create'), # 게시글 목록 조회
path('posts/create/', views.PostCreateView.as_view(), name='post-create'), # 게시글 작성
path('posts/<int:pk>/', views.PostView.as_view(), name='post-detail'), # 게시글 상세 조회
path('posts/<int:pk>/edit/', views.PostEditView.as_view(), name='post-edit'), # 게시글 수정
path('posts/<int:pk>/delete/', views.PostDeleteView.as_view(), name='post-delete'), # 게시글 삭제
path('comments/<int:pk>/delete/', views.CommentDeleteView.as_view(), name='comment-delete'), # 댓글 삭제
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
1-4) forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['content']
widgets = {
'content': forms.Textarea(attrs={'placeholder': '댓글을 입력하세요...', 'rows': 3}),
}
1-5) 결과물

이렇게 로그인하면 메인페이지 형식으로 보여줍니다
로그인을 했다면 환영합니다와 함께 닉네임을 알려주고
마이페이지 로그아웃 글작성하기가 보이도록 설정하였으며
비로그인시에는 이러한것들이 안보이고 로그인 유도글을 넣었습니다. 밑에는 이제 이전에 만들었던 전적검색기능과 스트리머 전적게시판으로 이동할 버튼을 만들었고 밑에는 게시글들을 보여주는 간단하지만 구현한 기능들을 전부 보여주도록 하였습니다.
ps) 너무 볼품없긴하네요..
2) 마이페이지 기능 구현
2-1) views.py
마이페이지에는 4가지 기능이 구현되어야합니다.
(1)프로필 이미지 변경
(2)닉네임 변경
(3)내 작성글 보기
(4)내 작성 댓글 보기
작성글과 댓글의 경우 너무 많아지는 것에 대비해서 django의 페이지네이션 기능을 추가하였습니다.
@method_decorator(login_required, name='dispatch')
class ProfileView(View):
def get(self, request):
# 사용자가 작성한 글 가져오기
user_posts = Post.objects.filter(author=request.user).order_by('-created_at')
post_paginator = Paginator(user_posts, 10) # 10개씩 페이지네이션
post_page_number = request.GET.get('post_page')
post_page = post_paginator.get_page(post_page_number)
# 사용자가 작성한 댓글 가져오기
user_comments = Comment.objects.filter(author=request.user).order_by('-created_at')
comment_paginator = Paginator(user_comments, 10) # 10개씩 페이지네이션
comment_page_number = request.GET.get('comment_page')
comment_page = comment_paginator.get_page(comment_page_number)
return render(request, 'users/profile.html', {
'post_page': post_page,
'comment_page': comment_page,
})
@method_decorator(login_required, name='dispatch')
class ProfileEditView(View):
def get(self, request):
form = ProfileForm(instance=request.user)
return render(request, 'users/profile_edit.html', {'form': form})
def post(self, request):
form = ProfileForm(request.POST, request.FILES, instance=request.user)
if form.is_valid():
user = form.save(commit=False) # 데이터베이스 저장 전 임시 저장
# 닉네임이나 이미지를 변경하지 않은 경우 기존 값을 유지
if not form.cleaned_data.get('user_img'): # 이미지가 없으면
user.user_img = request.user.user_img # 기존 이미지 유지
if not form.cleaned_data.get('nickname'): # 닉네임이 없으면
user.nickname = request.user.nickname # 기존 닉네임 유지
user.save() # 변경 사항 저장
return redirect('profile')
# 폼 유효성 검사 실패 시 오류 메시지 표시
return render(request, 'users/profile_edit.html', {'form': form, 'errors': form.errors})
1) ProfileView를 통해 작성한 게시글과 댓글에 대한 정보를 가져오고 이를 10개씩 나누어서 저장하도록 하였습니다.
2) ProfileEditView를 통해 post요청을 통해 이미지 혹은 닉네임 변경을 실시할경우를 대비해서 post요청과 files요청하는 폼을 처리하도록 하였습니다.
3) 폼의 타당성 여부를 판단하고 닉네임과 이미지를 둘다 변경하는 것이 아닌 선택사항으로 냅두어서 하나만 변경하였다면 저장할때 기존 값을 그대로 유지하도록 하였습니다. 왜냐하면 하나씩 변경하기에는 더 많은 폼을 가져와야하고 한번에 무조건 바꿔야한다면 사용자 편의성을 해치는 길이라 생각해서 이렇게 정하였습니다.
2-2) models.py
이미지를 변경하다보면 db에는 따로 저장이 되지는 않지만 컴퓨터에 계속해서 누적해서 저장되는 현상이 있어서 개인프로젝트 단위에서는 큰 상관이 없지만 필요없는 데이터가 누적해서 쌓이는 것과 같기때문에 변경시 저장된 사진을 덮어쓰도록 해야합니다.
def save(self, *args, **kwargs):
# 기존 이미지가 있으면 삭제
if self.pk:
try:
old_user = User.objects.get(pk=self.pk)
if old_user.user_img and old_user.user_img != self.user_img:
old_user.user_img.delete(save=False)
except User.DoesNotExist:
pass # 새 객체인 경우 무시합니다
super().save(*args, **kwargs) # 부모 클래스의 save 메서드 호출
1) 기존 커스텀 유저 모델 정의 클래스에서 이것을 추가하여서 변경전의 사진을 변경후의 사진으로 덮어쓰도록 하였습니다.
2-3) 결과물

마이페이지를 통해 본인의 이메일 닉네임 포인트를 보여주도록하였으며 여기에 더해 작성글과 작성 댓글을 보여주는 간단하게 기능을 구현하였습니다.
작성글 제목만을 보여주어서 이를 클릭하면 해당 게시글로 이동하도록 하였습니다.
마지막으로 프로필 수정을 누르게 되면
이미지와 닉네임을 변경하도록 하였습니다.
'Django 개인프로젝트 > 첫번째 프로젝트' 카테고리의 다른 글
| 11. Django와 RiotAPI를 이용한 스트리머 전적검색 및 저장11. Django와 RiotAPI를 이용한 스트리머 전적검색 및 저장 (2) | 2024.11.25 |
|---|---|
| 9. Django와 html을 이용해 로그인후 글쓰기 연동 (2) | 2024.11.11 |
| 8. 회원가입 기능 개선 (1) | 2024.11.08 |
| 7. html 템플릿 Django프로젝트 (1) (3) | 2024.11.05 |
| 6. Django와 RiotAPI를 이용한 전적정보 저장 (0) | 2024.10.30 |