티스토리 뷰

반응형

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 때문에 로그인 창으로 넘어간다. 이때 로그인을 하면 오류가 뜬다.

  1. 로그인을 안 한 상태로 댓글을 작성하면 @login_required를 먼저 만난다.
  2. next parameter를 활용해서 로그인 로직으로 보낸다.
  3. 이때 next parameter가 있으면 그 페이지로 돌아간다.
  4. 돌아올 때 redirect를 POST로 못시키고 GET으로만 시킬 수 있다.
  5. 그래서 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를 추가해 준다.

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
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
글 보관함