포스트 수정 페이지 만들기
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 %}