어떤 문제가 있을까?
답변등록 및 수정을 하면 질문상세 화면의 브라우저의 스크롤바가 항상 페이지 상단으로 고정된다. 코드 오류는 아니지만 답변을 작성한 다음에는 답변 위치에 스크롤이 있어야 자연스럽다. 가장 쉽게 해결해 보자.
앵커 엘리먼트로 스크롤 문제 해결
HTML 에서는 URL을 호출하면 원하는 위치로 스크롤을 이동시키는 엥커 엘리먼트 <a>가 있다. 예를 들어 HTML 중간에 <a name="django"></a>와 같이 앵커 엘리먼트를 위치시키고, 해당 HTML을 호출하는 URL에 뒤에 #django를 붙여 주면 바로 해당 앵커 엘리먼트 위치로 스크롤이 이동한다. 이 원리로 스크롤 초기화 문제를 해결할 수 있다. 그러면 답변등록, 답변수정, 댓글등록, 댓글수정 순으로 앵커 엘리먼트를 적용해 보자.
답변등록, 답변수정 시 앵커 기능 추가
(1단계) 질문상세 화면에 앵커 엘리먼트 추가
질문상세 화면에 답변등록, 답변수정 할 때 이동해야 할 앵커 엘리먼트를 추가한다.
# D:\projects\mysite\templates\pybo\question_detail.html (....생략...) <h5 class="border-bottom my-3 py-2"> {{ question.answer_set.count }} 개의 답변이 있습니다. </h5> {% for answer in question.answer_set.all %} <a name="answer_{{answer.id}}"></a> <div class="row my-3"> (....생략...) |
답변이 반복되어 표시되는 for문 바로 다음에 <a name="answer_{{answer.id}}"></a>와 같이 앵커 엘리먼트를 추가했다. 앵커 엘리먼트의 name 속성은 유일해야 하므로 답변 id를 사용했다.
(2단계) 앵커 엘리먼트로 이동할 수 있도록 redirect 수정
이제 답변을 등록하거나 수정할 때 1단계에서 지정한 앵커 엘리먼트로 이동하도록 코드를 수정한다.
# 답변등록, 답변수정 후 리다이렉트 코드 예)
return redirect('pybo:detail', question_id=question.id)
# 앵커 엘리먼트를 적용한 코드 예)
return redirect( '{}#answer_{}'.format(
resolve_url('pybo:detail', question_id=question.id), answer.id ))
pybo:detail에 #answer_2와 같은 앵커를 추가하기 위해 format과 resolve_url 함수를 사용했다. resolve_url 함수는 실제 호출되는 URL을 문자열로 반환하는 장고 함수이다. 위를 참고하여 answer_views.py 파일의 answer_create, answer_modify 함수를 수정해 보자.
# D:\projects\mysite\pybo\views\answer_views.py from django.contrib import messages from django.contrib.auth.decorators import login_required from django.shortcuts import render, get_object_or_404, redirect, resolve_url from django.utils import timezone from ..forms import QuestionForm, AnswerForm from ..models import Question, Answer @login_required(login_url='common:login') def answer_create(request, question_id): """ 답변등록 """ question = get_object_or_404(Question, pk=question_id) if request.method == 'POST': form = AnswerForm(request.POST) if form.is_valid(): answer = form.save(commit=False) answer.author = request.user answer.create_date = timezone.now() answer.question = question answer.save() # return redirect('pybo:detail', question_id=question_id) return redirect('{}#answer_{}'.format( resolve_url('pybo:detail', question_id=question.id), answer.id)) else: form = QuestionForm() context = {'question': question, 'form': form} return render(request, 'pybo/question_detail.html', context) @login_required(login_url='common:login') def answer_modify(request, answer_id): """ 답변수정 """ answer = get_object_or_404(Answer, pk=answer_id) if request.user != answer.author: messages.error(request, '수정권한이 없습니다') return redirect('pybo:detail', question_id=answer.question.id) if request.method == "POST": form = AnswerForm(request.POST, instance=answer) if form.is_valid(): answer = form.save(commit=False) answer.author = request.user answer.modify_date = timezone.now() # 수정일시 저장 answer.save() # return redirect('pybo:detail', question_id=answer.question.id) return redirect('{}#answer_{}'.format( resolve_url('pybo:detail', question_id=answer.question.id), answer.id)) else: form = AnswerForm(instance=answer) context = {'answer': answer, 'form': form} return render(request, 'pybo/answer_form.html', context) (... 생략...) |
수정한 후 답변등록 시 스크롤이 지정한 앵커 엘리먼트로 이동하는지 확인해 보자.
상세화면 URL에 #answer_8 가 추가되었고, 스크롤 위치가 정상적으로 이동했음을 확인할 수 있다.
댓글에 앵커 기능 추가
(1단계) 댓글 앵커 엘리먼트 추가
댓글이 반복되는 구간에 댓글 앵커 기능을 추가한다.
# D:\projects\mysite\templates\pybo\question_detail.html (...생략...) <!-- 질문 댓글 Start --> {% if question.comment_set.count > 0%} <div class="mt-3"> {% for comment in question.comment_set.all %} <a name="comment_{{comment.id}}"></a> <div class="comment py-2 text-muted"> <span style="white-space: pre-line;">{{ comment.content }}</span> <span> - {{ comment.author }}, {{ comment.create_date }} {% if comment.modify_date %} (수정: {{ comment.modify_date }}) {% endif %} </span> {% if request.user == comment.author %} <a href="{% url 'pybo:comment_modify_question' comment.id %}" class="small">수정</a> <a href="#" class="small delete" data-uri="{% url 'pybo:comment_delete_question' comment.id %}">삭제</a> {% endif %} </div> {% endfor %} </div> {% endif %} <div> (... 생략 ...) <!-- 답변 댓글 Start --> {% if answer.comment_set.count > 0%} <div class="mt-3"> {% for comment in answer.comment_set.all %} <a name="comment_{{comment.id}}"></a> <div class="comment py-2 text-muted"> <span style="white-space: pre-line;">{{ comment.content }}</span> <span> - {{ comment.author }}, {{ comment.create_date }} {% if comment.modify_date %} (수정: {{ comment.modify_date }}) {% endif %} </span> {% if request.user == comment.author %} <a href="{% url 'pybo:comment_modify_answer' comment.id %}" class="small">수정</a> <a href="#" class="small delete" data-uri="{% url 'pybo:comment_delete_answer' comment.id %}">삭제</a> {% endif %} </div> {% endfor %} </div> {% endif %} <div> <a href="{% url 'pybo:comment_create_question' question.id %}" class="small"> <small>댓글 추가...</small> </a> </div> <!-- 답변 댓글 End --> </div> </div> </div> </div> {% endfor %} (... 생략...) |
질문, 답변 댓글에 모두 앵커 엘리먼트를 추가했다.
(2단계) redirect 함수 수정
comment_views.py 파일을 수정한다.
# D:\projects\mysite\pybo\views\comment_views.py from django.contrib import messages from django.contrib.auth.decorators import login_required from django.shortcuts import render, get_object_or_404, redirect, resolve_url from django.utils import timezone from ..forms import CommentForm from ..models import Question, Answer, Comment @login_required(login_url='common:login') def comment_create_question(request, question_id): # pybo 질문 댓글 등록 question = get_object_or_404(Question, pk=question_id) if request.method == 'POST': form = CommentForm(request.POST) if form.is_valid(): comment = form.save(commit=False) comment.author = request.user comment.create_date = timezone.now() comment.question = question comment.save() # return redirect('pybo:detail', question_id=question.id) return redirect('{}#comment_{}'.format( resolve_url('pybo:detail', question_id=question.id), comment.id)) else: form = CommentForm() context = {'form': form} return render(request, 'pybo/comment_form.html', context) @login_required(login_url='common:login') def comment_modify_question(request, comment_id): """ pbyo 질문 댓글 수정 """ comment = get_object_or_404(Comment, pk=comment_id) if request.user != comment.author: messages.error(request, '수정권한이 없습니다') return redirect('pybo:detail', question_id=comment.question.id) if request.method == "POST": form = CommentForm(request.POST, instance=comment) if form.is_valid(): comment = form.save(commit=False) comment.author = request.user comment.modify_date = timezone.now() # 수정일시 저장 comment.save() # return redirect('pybo:detail', question_id=comment.question.id) return redirect('{}#comment_{}'.format( resolve_url('pybo:detail', question_id=comment.question.id), comment.id)) else: form = CommentForm(instance=comment) context = {'form': form} return render(request, 'pybo/comment_form.html', context) (... 생략...) @login_required(login_url='common:login') def comment_create_answer(request, answer_id): # pybo 답변 댓글 등록 answer = get_object_or_404(Question, pk=answer_id) if request.method == 'POST': form = CommentForm(request.POST) if form.is_valid(): comment = form.save(commit=False) comment.author = request.user comment.create_date = timezone.now() comment.answer = answer comment.save() # return redirect('pybo:detail', question_id=comment.answer.question.id) return redirect('{}#comment_{}'.format( resolve_url('pybo:detail', question_id=comment.answer.question.id), comment.id)) else: form = CommentForm() context = {'form': form} return render(request, 'pybo/comment_form.html', context) @login_required(login_url='common:login') def comment_modify_answer(request, comment_id): """ pbyo 답변 댓글 수정 """ comment = get_object_or_404(Comment, pk=comment_id) if request.user != comment.author: messages.error(request, '댓글수정권한이 없습니다') return redirect('pybo:detail', question_id=comment.answer.question.id) if request.method == "POST": form = CommentForm(request.POST, instance=comment) if form.is_valid(): comment = form.save(commit=False) comment.author = request.user comment.modify_date = timezone.now() # 수정일시 저장 comment.save() # return redirect('pybo:detail', question_id=comment.answer.question.id) return redirect('{}#comment_{}'.format( resolve_url('pybo:detail', question_id=comment.answer.question.id), comment.id)) else: form = CommentForm(instance=comment) context = {'form': form} return render(request, 'pybo/comment_form.html', context) (... 생략...) |
이제 댓글을 작성한 다음 화면이 해당 앵커 엘리먼트로 이동하는지 확인해 보자.
댓글을 작성하면 URL에 #comment_4가 추가되고, 웹 브라우저의 스크롤바가 해당 위치로 이동하는 것을 확인할 수 있다.
여기까지.
'개발자모드 > 혼자공부하는파이썬' 카테고리의 다른 글
[파이썬/장고#27] 검색, 정렬 기능 추가하기 (Q, filter, distinct 함수) (1) | 2022.10.07 |
---|---|
[파이썬/장고#26] 마크다운기능 - 주요문법 (순서없는목록, 순서있는목록, 강조, 링크, 코드, 인용) (1) | 2022.09.30 |
[파이썬/장고#24] 게시판 추천(좋아요) 기능 추가 (ft. 질문추천, 답변추천) (0) | 2022.09.28 |
[파이썬/장고#23] 유지보수하기 좋은 구조로 개선 (views.py 파일 분리, urls.py 파일 수정) (0) | 2022.09.26 |
[파이썬/장고#22] 게시판 댓글 기능 추가 (ft. 장고기능개발 패턴 정리) (0) | 2022.09.22 |
댓글