728x90
반응형
models.py
from django.contrib.auth.models import User
from django.db import models
class Article(models.Model):
writer = models.ForeignKey(User, on_delete=models.SET_NULL, related_name='article', null=True)
title = models.CharField(max_length=200, null=True)
image = models.ImageField(upload_to='article/', null=False)
content = models.TextField(null=True)
created_at = models.DateField(auto_now_add=True, null=True)
- article model 정의
- writer 필드는 User 탈퇴시에도 게시글은 삭제되지 않음 (SET_NULL)
- 이미지는 article/ 경로 밑에 저장 하고 항상 넣도록 하기(null=False)
- 데이터 생성 시 자동으로 생성시간 저장됨(auto_now_add=True)
forms.py
from django.forms import ModelForm
from articleapp.models import Article
class ArticleCreationForm(ModelForm):
class Meta:
model = Article
fields = ['title', 'image', 'content']
- modelform 추가
- title, image, content 필드 구성
마이그레이션
python manage.py makemigrations
python manage.py migrate
- 생성한 model DB에 반영
urls.py
from django.urls import path
from django.views.generic import TemplateView
from articleapp.views import ArticleCreateView, ArticleDetailView, ArticleUpdateView, ArticleDeleteView
app_name = 'articleapp'
urlpatterns = [
path('list/', TemplateView.as_view(template_name='articleapp/list.html'), name='list'),
path('create/', ArticleCreateView.as_view(), name='create'),
path('detail/<int:pk>', ArticleDetailView.as_view(), name='detail'),
path('update/<int:pk>', ArticleUpdateView.as_view(), name='update'),
path('delete/<int:pk>', ArticleDeleteView.as_view(), name='delete'),
]
- app_name 추가
- create, detail, update, delete url 지정
views.py
from _testcapi import get_kwargs
from django.urls import reverse, reverse_lazy
from Tools.scripts.texi2html import kwprog
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
# Create your views here.
from django.utils.decorators import method_decorator
from django.views.generic import CreateView, DetailView, UpdateView, DeleteView
from articleapp.decorators import article_ownership_required
from articleapp.forms import ArticleCreationForm
from articleapp.models import Article
# login_required : 게시물 작성 시 항상 로그인되어 있어야함
@method_decorator(login_required(), 'get')
@method_decorator(login_required(), 'post')
class ArticleCreateView(CreateView):
model = Article
form_class = ArticleCreationForm
template_name = 'articleapp/create.html'
# 서버에서 writer 지정
def form_valid(self, form):
# 같은 writer와 로그인한 user인지 체크
temp_article = form.save(commit=False)
temp_article.writer = self.request.user
temp_article.save()
return super().form_valid(form)
def get_success_url(self):
return reverse('articleapp:detail', kwargs={'pk': self.object.pk})
class ArticleDetailView(DetailView):
model = Article
context_object_name = 'target_article'
template_name = 'articleapp/detail.html'
@method_decorator(article_ownership_required, 'get')
@method_decorator(article_ownership_required, 'post')
class ArticleUpdateView(UpdateView):
model = Article
context_object_name = 'target_article'
form_class = ArticleCreationForm
template_name = 'articleapp/update.html'
def get_success_url(self):
# 성공시 detail.html로 이동
return reverse('articleapp:detail', kwargs={'pk': self.object.pk})
@method_decorator(article_ownership_required, 'get')
@method_decorator(article_ownership_required, 'post')
class ArticleDeleteView(DeleteView):
model = Article
context_object_name = 'target_article'
success_url = reverse_lazy('articleapp:list')
template_name = 'articleapp/delete.html'
- createview, detailview, updateview, deleteview 추가
- login_required()는 게시물 작성시 항상 로그인이 되어있음 확인
- 게시물 작성, 수정, 삭제 후 detail로 이동
- 내가 만든 article_ownership_required 데코레이터 추가
decorators.py
from django.contrib.auth.models import User
from django.http import HttpResponseForbidden
import articleapp
from articleapp.models import Article
def article_ownership_required(func):
def decorated(request, *args, **kwargs):
article = Article.objects.get(pk=kwargs['pk'])
if not article.writer == request.user:
return HttpResponseForbidden()
return func(request, *args, **kwargs)
return decorated
- article 작성 게시자가 request된 user인지 확인
- 같은 user가 아니면 forbidden페이지 return
list.html
{% extends 'base.html' %}
{% load static %}
{% block content %}
<style>
.container div {
width: 250px;
background-color: antiquewhite;
display: flex;
justify-content: center;
align-items: center;
border-radius: 1rem;
}
.container img{
width: 100%;
border-radius: 1rem;
}
</style>
<div class="container">
<div >
<img src="https://picsum.photos/200/300">
</div>
<div >
<img src="https://picsum.photos/200/340">
</div>
<div >
<img src="https://picsum.photos/200/260">
</div>
<div >
<img src="https://picsum.photos/200/400">
</div>
<div >
<img src="https://picsum.photos/200/300">
</div>
<div >
<img src="https://picsum.photos/200/250">
</div>
<div >
<img src="https://picsum.photos/200/200">
</div>
<div >
<img src="https://picsum.photos/200/440">
</div>
<div >
<img src="https://picsum.photos/200/300">
</div>
<div >
<img src="https://picsum.photos/200/200">
</div>
<div >
<img src="https://picsum.photos/200/350">
</div>
<div >
<img src="https://picsum.photos/200/430">
</div>
<div >
<img src="https://picsum.photos/200/300">
</div>
<div >
<img src="https://picsum.photos/200/400">
</div>
<div >
<img src="https://picsum.photos/200/280">
</div>
</div>
<div style="text-align:center">
<a href="{% url 'articleapp:create' %}" class="btn btn-dark rounded-pill col-3 mt-3 mb-3">
Create Article
</a>
</div>
<script src="{% static 'js/magicgrid.js' %}"></script>
{% endblock %}
- 게시물 추가할 수 있는 create Article 버튼 추가
- 생성 버튼 추가된 페이지
create.html
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
<div style="text-align:center; max-width: 500px; margin: 4rem auto">
<div class="mb-4">
<h4>Article Create</h4>
</div>
<form action="{% url 'articleapp:create' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" class="btn btn-dark rounded-pill col-6 mt-3">
</form>
</div>
{% endblock %}
- article 작성 페이지
- 작성 form 생성 및 url 지정
- 글 작성 페이지
detail.html
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
<div>
<div style="text-align: center; max-width: 500px; margin: 4rem auto;">
<h1>{{ target_article.title }}</h1>
<img src="{{ target_article.image.url }}" alt="">
<p>
{{ target_article.content }}
</p>
<a href="{% url 'articleapp:update' pk=target_article.pk %}">
<p>Update Article</p>
</a>
<a href="{% url 'articleapp:delete' pk=target_article.pk %}">
<p>Delete Article</p>
</a>
</div>
</div>
{% endblock %}
- 게시물의 title, 이미지, 내용 보여주는 detail 페이지
- 수정, 삭제 버튼 추가
- 작성한 글 보기
- detail 페이지
update.html
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
<div style="text-align:center; max-width: 500px; margin: 4rem auto">
<div class="mb-4">
<h4>Article Update</h4>
</div>
<form action="{% url 'articleapp:update' pk=target_article.pk %}" method="post">
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" class="btn btn-dark rounded-pill col-6 mt-3">
</form>
</div>
{% endblock %}
- 게시물 수정 페이지
- 수정 form 및 update url 로 지정
delete.html
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
<div style="text-align:center; max-width: 500px; margin: 4rem auto">
<div class="mb-4">
<h4>Delete Article : {{ target_article.title }}</h4>
</div>
<form action="{% url 'articleapp:delete' pk=target_article.pk %}" method="post">
{% csrf_token %}
<input type="submit" class="btn btn-danger rounded-pill col-6 mt-3">
</form>
</div>
{% endblock %}
- 게시물 삭제 페이지
- title, 삭제 버튼 추가
- 글 삭제 페이지
- 제출 버튼 누르면 글 삭제
- 삭제 후 url 주소로 들어가면 not found error 뜨면서 성공적으로 삭제완료 확인
반응형
'Backend > Django' 카테고리의 다른 글
django 25. Mixin 소개 및 Commentapp 구현 (0) | 2021.10.12 |
---|---|
django 24. ListView, Pagination 소개 및 적용 (0) | 2021.10.10 |
django 22. MagicGrid 소개 및 Articleapp 시작 (0) | 2021.10.10 |
django 21. get_success_url 함수 그리고 리팩토링 (0) | 2021.10.06 |
django 20. Profileapp 마무리 (0) | 2021.10.06 |