티스토리 뷰
0. 들어가면서
'[Django]데이터베이스관리(1:N)_User와 Article' 부분을 우선 학습을 하고 오는걸 권장한다. 유저와 게시물과의 관계를 다시보면, 1:N 관계 연결은 ForeignKey가 N부분인 article에 들어간다. 그리고 ForeignKey는 on_delete도 같이 작성해 줘야한다. 참조 무결성에 관련된 내용으로 Reporter 삭제 시 글들이 어떻게 될지를 설정하는 것이다. 반대의 경우는 Article 삭제 시 Repoter에는 변화가 없다.
댓글 기능은 articles_article : articles_comment = 1 : N 이다.
comment를 만들 때 주의 할 점
- 작성창은 detail 페이지에 있다.
- 그러나 처리 부분은 다른 view 함수에서 하고 detail 페이지로 보낸다.
1. models.py
class Comment(models.Model):
content = models.TextField()
article = models.ForeignKey(Article, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) 이 부분을 안 넣으면 익명으로 글 쓰기가 가능하다.
위의 내용을 추가하고 migration을 해줘서 DB에 추가한다. 그러면 아래와 같이 태이블이 생성된다.
articles_comment (테이블 이름)
id | content | article_id | user_id |
여기도 앞과 마찬가지로 저장 로직에서 form.save(commit=False)를 해서 article_id와 user_id를 따로 가져와서 저장을 해줘야한다.
2. detail.html에 form 만들기
댓글 작성창은 detail 안에 만들어 지기 때문에 detail.html에서 모델폼으로 만들자.
1. forms.py 추가
from .models import Article, Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['content']
2. views.py의 update 부분 추가
comment를 html에서 보내주려면 context에 CommentForm을 담아서 같이 보내줘야 한다.
def detail(request, pk):
article = get_object_or_404(Article, pk=pk)
form = CommentForm()
context = {
'article': article,
'form': form
}
return render(request, 'articles/detail.html', context)
3. detail.html 추가
보여지는 부분은 아래에서 다시 추가 할거고 여기서는 form만 추가한다. 여기서 주의 할 것은 form의 action부분을 빈칸으로 남기면 안되고 action정의를 해줘야 한다.
{% extends 'base.html' %}
{% load bootstrap4 %}
~
~
~
<hr>
<form action="{% url 'articles:comments_create' article.pk %}" method="POST">
{% csrf_token %}
{% bootstrap_form form %}
<button class="btn btn-primary">작성</button>
</form>
~
~
~
detail함수는 단순히 보여주는 작업만 한다. (POST, GET 분기 없음) 따라서 action부분을 안적으면 detail부분으로 다시 돌아 올 수가 없다. 그렇기 때문에 댓글 작성 링크를 따로 url 설정을 해줘야한다.
action의
{% url 'articles:comments_create' article.pk %}
이 부분은 아래의 댓글 생성 로직의 1번인 url을 작성한 후에 추가해 주는게 맞다. 여기서는 편의상 미리 적은거다.
3. 댓글 생성 로직 (MTV)
1. articles/urls.py
path('<int:pk>/comments/', views.comments_create, name='comments_create')
url적을 때 생성 부분이라서 '<int:pk>/comments/create/'로 적어도 되지만 여기서는 목록페이지가 따로 없기 때문에 create를 빼고 url을 생성해도 굳이 상관이 없다.
2. views.py
@require_POST
@login_required
def comments_create(request, pk):
article = get_object_or_404(Article, pk=pk)
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.user = request.user
comment.article = article
comment.save()
return redirect('articles:detail', article.pk)
바로 save()를 하면 안된다. article_id와 user_id를 가져와서 저장을 해줘야한다.
@require_POST와 @login_required를 같이 사용 시 발생하는 문제점
로그인을 하지 않으 채로 댓글을 작성하면 문제가 발생한다. 상세히 적어보면, 로그인을 안 한채로 댓글을 작성하면 @login_required 때문에 로그인 창으로 넘어간다. 이때 로그인을 하면 오류가 뜬다.
- 로그인을 안 한 상태로 댓글을 작성하면 @login_required를 먼저 만난다.
- next parameter를 활용해서 로그인 로직으로 보낸다.
- 이때 next parameter가 있으면 그 페이지로 돌아간다.
- 돌아올 때 redirect를 POST로 못시키고 GET으로만 시킬 수 있다.
- 그래서 GET으로 돌아오니 @require_POST를 만나서 에러가 뜬다.
해결책) @login_required를 빼고 아래와 같이 if를 추가한다.(next parameter은 없이 진행한다.)
+ message framework(메세지 프레임워크)를 추가 하기 가능하다.
@require_POST
def comments_create(request, pk):
if request.user.is_authenticated:
article = get_object_or_404(Article, pk=pk)
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.user = request.user
comment.article = article
comment.save()
return redirect('articles:detail', article.pk)
else:
# 1. next parameter 없이 진행.
messages.warning(request, '댓글 작성을 위해서는 로그인이 필요합니다.')
return redirect('accounts:login')
3. detail.html
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block body %}
<h1>{{ article.pk }}번 글</h1>
~
~
~
</form>
<a href="{% url 'articles:update' article.pk %}"><button class="btn btn-primary">수정</button></a>
{% endif %}
<hr>
<h3>댓글</h3>
{% for comment in article.comment_set.all %}
<li>{{ comment.user.username }} : {{ comment.content }}</li>
{% endfor %}
<hr>
<form action="{% url 'articles:comments_create' article.pk %}" method="POST">
{% csrf_token %}
{% bootstrap_form form %}
<button class="btn btn-primary">작성</button>
</form>
{% endblock %}
4. style(bootstrap으로 꾸미기)
1. accounts/templates/accounts/login.html
{% load bootstrap4 %}
{% block body %}
<h2 class="text-center">로그인</h2>
<form action="" method="POST">
{% csrf_token %}
{% bootstrap_form form %}
<h2 class="text-center">로그인</h2>
이 부분을 추가함
2. accounts/templates/accounts/signup.html
{% load bootstrap4 %}
{% block body %}
<h2 class="text-center">회원가입</h2>
<form action="" method="POST">
{% csrf_token %}
{% bootstrap_form form %}
<h2 class="text-center">회원가입</h2>
이 부분을 추가함
3. articles/forms.py
class CommentForm(forms.ModelForm):
content = forms.CharField(
label='댓글',
widget=forms.Textarea(
attrs={
'rows': 2,
}
)
)
class Meta:
model = Comment
fields = ['content']
이 부분은 modelform부분을 참고하자
4. articles/templates/articles/detail.html
{% load bootstrap4 %}
{% block body %}
<h1>{{ article.pk }} : {{ article.title }}</h1>
<p>{{ article.user.username }}님</p>
<p class="text-secondary">{{ article.created_at }} | 수정: {{ article.updated_at }}</p>
{% if article.user == request.user %}
<div class="text-right">
<form action="{% url 'articles:delete' article.pk %}" method="POST" class="d-inline">
{% csrf_token %}
<button class="btn btn-secondary">삭제</button>
</form>
<a href="{% url 'articles:update' article.pk %}"><button class="btn btn-secondary">수정</button></a>
</div>
{% endif %}
<hr>
<p>{{ article.content }}</p>
<hr>
{% for comment in article.comment_set.all %}
<li>{{ comment.user.username }} : {{ comment.content }}</li>
{% endfor %}
이쁘게 div안에 넣어준다. 그리고 배치를 약간 바꿔준다.
5. articles/templates/articles/index.html
{% extends 'base.html' %}
{% block body %}
<h2 class="text-center">게시판 목록</h2>
<a href="{% url 'articles:create' %}"><button class="btn btn-primary float-right my-2">글 쓰기</button></a>
<table class="table">
<thead class="thead-light">
<tr>
h2 부분에 class를 추가해 준다.
'Web > Django' 카테고리의 다른 글
[Django]데이터베이스관리(N:M)_좋아요 기능 구현 (32) | 2020.08.28 |
---|---|
[Django]데이터베이스관리(N:M)_기초개념 (0) | 2020.08.27 |
[Django]데이터베이스관리(1:N)_User와 Article (1) | 2020.08.23 |
[Django]사용자인증관리_회원탈퇴 (0) | 2020.08.16 |
[Django]base.html_최종 (31) | 2020.08.14 |
- Total
- Today
- Yesterday
- react
- 자연어처리
- 자료구조
- DFS
- error:0308010C:digital envelope routines::unsupported
- typescript
- read_csv
- BFS
- nextjs autoFocus
- JavaScript
- nodejs
- 클라우데라
- useHistory 안됨
- react autoFocus
- Vue
- UserCreationForm
- Python
- next.config.js
- useState
- Express
- Queue
- django
- login
- Deque
- vuejs
- pandas
- mongoDB
- NextJS
- TensorFlow
- logout
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |