포스트 수정 페이지 만들기

Posted on 2021-08-25 by GKSRUDTN99
Django로 웹사이트 만들기 장고

테스트 코드 작성

# blog/tests.py
(...생략...)
    def test_update_post(self):
        update_post_url = f'/blog/update_post/{self.post_003.pk}/'

        # 로그인하지 않은 경우
        response = self.client.get(update_post_url)
        self.assertNotEqual(response.status_code, 200)

        # 로그인은 했지만 작성자가 아닌 경우
        self.assertNotEqual(self.post_003.author, self.user_trump)
        self.client.login(
            username=self.user_trump.username,
            password='somepassword'
        )
        response = self.client.get(update_post_url)
        self.assertEqual(response.status_code, 403)

        # 작성자(obama)가 접근하는 경우
        self.client.login(
            username=self.post_003.author.username,
            password='somepassword'
        )
        response = self.client.get(update_post_url)
        self.assertEqual(response.status_code, 200)
        soup = BeautifulSoup(response.content, 'html.parser')

        self.assertEqaul('Edit Post - Blog', soup.title.text)
        main_area = soup.find('div', id='main-area')
        self.assertIn('Edit Post', main_area.text)

        response = self.client.post(
            update_post_url,
            {
                'title': '세 번째 포스트를 수정했습니다. ',
                'content': '안녕 세계? 우리는 하나!',
                'category': self.category_music.pk
            },
            follow=True
        )
        soup = BeautifulSoup(response.content, 'html.parser')
        main_area = soup.find('div', id='main-area')
        self.assertIn('세 번째 포스트를 수정했습니다.', main_area.text)
        self.assertIn('안녕 세계? 우리는 하나!', main_area.text)
        self.assertIn(self.category_music.name, main_area.text)

urls.py 수정

'/blog/update_post/포스트의_pk/'로 접근할 때 blog/views.py의 PostUpdate클래스를 사용하도록 수정한다.

from django.contrib import admin
from django.urls import path
from . import views

urlpatterns = [
    path('update_post/<int:pk>/', views.PostUpdate.as_view()),
    path('create_post/', views.PostCreate.as_view()),
    path('tag/<str:slug>/', views.tag_page),
    path('category/<str:slug>/', views.category_page),
    path('<int:pk>/', views.PostDetail.as_view()),
    path('', views.PostList.as_view()),
]

views.py

PostUpdate 클래스를 CBV 방식으로 구현한다.

# blog/views.py

class PostUpdate(LoginRequiredMixin, UpdateView):
    model = Post
    fields = ['title', 'hook_text', 'content', 'head_image', 'file_upload', 'category', 'tags']

위와 같이 작성한 뒤 테스트하면, trump로 로그인한 뒤 접근해도 접근이 가능하다고 한다.

작성자만 수정할 수 있도록 CBV에서 제공하는 dispatch()메서드를 활용한다.

dispatch()메서드는 방문자가 웹 사이트 서버에 GET 방식으로 요청했는지 POST 방식으로 요청했는지 판단하는 기능을 한다.
만약 권한이 없는 사용자가 PostUpdate를 사용하려고 한다면 GET(폼 페이지 요청) POST방식이든 상관없이 접근할 수 없게 해야한다.
따라서 dispatch()가 실행되는 순간 방문자가 포스트의 작성자가 맞는지 확인하도록 한다.

# blog/views.py
from django.core.exceptions import PermissionDenied
( ... )
class PostUpdate(LoginRequiredMixin, UpdateView):
    model = Post
    fields = ['title', 'hook_text', 'content', 'head_image', 'file_upload', 'category', 'tags']

    def dispatch(self, request, *args, **kwargs):
        if request.user.is_authenticated and request.user == self.get_object().author:
            return super(PostUpdate, self).dispatch(request, *args, **kwargs)
        else:
            raise PermissionDenied

위 코드에서 사용한 self.get_object()는 UpdateView 내의 메서드로, Post.objects.get(pk=pk)와 같은 역할을 한다.

템플릿 파일 지정하기.

PostUpdate는 UpdateView를 상속받아 생성한 클래스이다.

UpdateView 클래스는 템플릿들 중에서 <모델 이름>_form.html 파일을 자동으로 찾아 사용한다.

이렇게 되면, CreateView에서 사용한 템플릿과 같은 템플릿을 사용하게 되므로, template_name을 통해 사용자가 지정한 템플릿 파일을 사용하도록 한다.

class PostUpdate(LoginRequiredMixin, UpdateView):
    model = Post
    fields = ['title', 'hook_text', 'content', 'head_image', 'file_upload', 'category', 'tags']

    template_name = 'blog/post_update_form.html'

    def dispatch(self, request, *args, **kwargs):
        if request.user.is_authenticated and request.user == self.get_object().author:
            return super(PostUpdate, self).dispatch(request, *args, **kwargs)
        else:
            raise PermissionDenied

그 후에, post_update_form.html 템플릿 파일을 새로 만들고, 다음과 같이 작성한다.

{% extends 'blog/base_full_width.html' %}

{% block head_title %}Edit Post - Blog{% endblock %}


{% block main_area %}
    <h1>Edit Post</h1>
    <hr/>
    <form method="post" enctype="multipart/form-data">
        {% csrf_token %}
        <table>
            {{ form }}
        </table>
        <button type="submit" class="btn btn-primary float-end">Submit</button>
    </form>
{% endblock %}

포스트 상세 페이지에 수정 버튼 추가하기

버튼 만들기

<!--post_detail.html-->
{% extends 'blog/base.html' %}

{% block head_title %}
    {{ post.title }} - Blog
{% endblock %}

{% block main_area %}
    <!-- Post content-->
    <div id="post-area">
        <!-- Post header-->
        <header class="mb-4">
            <!-- Post title-->
            <h1 class="fw-bolder mb-1">{{ post.title }}</h1>
            {% if post.hook_text %}
                <h5 class="text-muted">{{ post.hook_text }}</h5>
            {% endif %}
            <!-- Post meta content-->
            <div class="text-muted fst-italic mb-2">Posted on {{ post.created_at }} by {{ post.author | upper }}
            {% if user.is_authenticated and user == post.author %}
                <a class="btn btn-info btn-sm float-end" href="/blog/update_post/{{ post.pk }}/" role="button"><i class="fas fa-pen"></i> Edit Post</a>
            {% endif %}
            </div>
            {% if post.category %}
                <span class="badge bg-secondary float-end">{{ post.category }}</span>
            {% else %}
                <span class="badge bg-secondary float-end">미분류</span>
            {% endif %}
            <!-- Post categories-->
            {% if post.tags.exists %}
                <i class="fas fa-tags"></i>
                {% for tag in post.tags.all %}
                    <a class="badge bg-secondary text-decoration-none link-light"
                       href="{{ tag.get_absolute_url }}">{{ tag }}</a>
                {% endfor %}
            {% endif %}
        </header>
        <!-- Preview image figure-->
        <figure class="mb-4">
            {% if post.head_image %}
                <img class="img-fluid rounded" src="{{ post.head_image.url }}" alt="{{ post }}"/>
            {% else %}
                <img class="img-fluid rounded" src="https://picsum.photos/seed/{{ post.id }}/800/200"
                     alt="random_image"/>
            {% endif %}
        </figure>
        <!-- Post content-->
        <section class="mb-5">
            <p>{{ post.content }}</p>
            {% if post.file_upload %}
                <a href="{{ post.file_upload.url }}" class="btn btn-outline-dark" role="button" download>
                    Download:
                    {% if post.get_file_ext == 'csv' %}
                        <i class="fas fa-file-csv"></i>
                    {% elif post.get_file_ext == 'xlsx' or post.get_file_ext == 'xls' %}
                        <i class="fas fa-file-excel"></i>
                    {% elif post.get_file_ext == 'docx' or post.get_file_ext == 'doc' %}
                        <i class="fas fa-file-word"></i>
                    {% else %}
                        <i class="fas fa-file"></i>
                    {% endif %}
                    {{ post.get_file_name }}
                </a>
            {% endif %}
        </section>
    </div>
    <!-- Comments section-->
    <div class="mb-5" id="comment-area">
        <div class="card bg-light">
            <div class="card-body">
                <!-- Comment form-->
                <form class="mb-4"><textarea class="form-control" rows="3"
                                             placeholder="Join the discussion and leave a comment!"></textarea>
                </form>
                <!-- Comment with nested comments-->
                <div class="d-flex mb-4">
                    <!-- Parent comment-->
                    <div class="flex-shrink-0"><img class="rounded-circle"
                                                    src="https://dummyimage.com/50x50/ced4da/6c757d.jpg"
                                                    alt="..."/></div>
                    <div class="ms-3">
                        <div class="fw-bold">Commenter Name</div>
                        If you're going to lead a space frontier, it has to be government; it'll never be
                        private enterprise. Because the space frontier is dangerous, and it's expensive, and it
                        has unquantified risks.
                        <!-- Child comment 1-->
                        <div class="d-flex mt-4">
                            <div class="flex-shrink-0"><img class="rounded-circle"
                                                            src="https://dummyimage.com/50x50/ced4da/6c757d.jpg"
                                                            alt="..."/></div>
                            <div class="ms-3">
                                <div class="fw-bold">Commenter Name</div>
                                And under those conditions, you cannot establish a capital-market evaluation of
                                that enterprise. You can't get investors.
                            </div>
                        </div>
                        <!-- Child comment 2-->
                        <div class="d-flex mt-4">
                            <div class="flex-shrink-0"><img class="rounded-circle"
                                                            src="https://dummyimage.com/50x50/ced4da/6c757d.jpg"
                                                            alt="..."/></div>
                            <div class="ms-3">
                                <div class="fw-bold">Commenter Name</div>
                                When you put money directly to a problem, it makes a good headline.
                            </div>
                        </div>
                    </div>
                </div>
                <!-- Single comment-->
                <div class="d-flex">
                    <div class="flex-shrink-0"><img class="rounded-circle"
                                                    src="https://dummyimage.com/50x50/ced4da/6c757d.jpg"
                                                    alt="..."/></div>
                    <div class="ms-3">
                        <div class="fw-bold">Commenter Name</div>
                        When I look at the universe and all the ways the universe wants to kill us, I find it
                        hard to reconcile that with statements of beneficence.
                    </div>
                </div>
            </div>
        </div>
    </div>
{% endblock %}