1. 다음 url이 실제 작동하도록 해주세요.
1.1 'blog/' : 블로그 글 목록
1.2 'blog/<int:pk>/' : 블로그 글 읽기
1.3 'blog/write/' : 블로그 글 작성
1.4 'blog/edit/<int:pk>/' : 블로그 글 업데이트
1.5 'blog/delete/<int:pk>/' : 블로그 글 삭제
###################################
앱이름: blog views 함수이름 html 파일이름 비고
'blog/' blog_list blog_list.html
'blog/<int:pk>' blog_details blog_details.html
'blog/write/' blog_write blog_write.html
'blog/edit/<int:pk>/' blog_edit blog_edit.html
'blog/delete/<int:pk>/' blog_delete blog_delete.html
이러한 제네릭 뷰를 활용하여 쉽게 로직을 구현할 수 있다.
from django.views.generic import (
ListView,
DetailView,
CreateView,
UpdateView,
DeleteView,
)
from .models import Post
from django.urls import reverse_lazy
from django.http import HttpResponse
class PostList(ListView):
model = Post
ordering = "-pk"
# 기본값은 최신 게시물이 맨 아래로 가기 때문에 pk를 기준으로 내림차순 정렬
# template_name = "blog/내가_원하는_파일명.html" # 기본값: blog/post_list.html
class PostDetail(DetailView):
model = Post
# template_name = "blog/내가_원하는_파일명.html" # 기본값: blog/post_detail.html
class PostCreate(CreateView):
model = Post
fields = "__all__"
success_url = reverse_lazy("blog_list")
# fields = ["title", "content", "image"]
# template_name = "blog/내가_원하는_파일명.html" # 기본값: blog/post_form.html
# reverse_lazy("blog_list")를 하는 이유는 object가 생성이 되고 나서 url로 이동해야 하는데 reverse는 함수이기 때문에 함수가 실행되는 시점에 url로 이동하게 되어버린다. 그래서 post가 생성된 후에 url로 이동하게 하기 위해서 기다리겠다는 함수가 reverse_lazy를 사용한다.
class PostUpdate(UpdateView):
model = Post
fields = "__all__"
success_url = reverse_lazy("blog_list")
# fields = ["title", "content", "image"]
# template_name = "blog/내가_원하는_파일명.html" # 기본값: blog/post_form.html
class PostDelete(DeleteView):
model = Post
success_url = reverse_lazy("blog_list")
# 삭제되고 다 완료되지 않은 상태에서 blog_list로 넘어가지 않도록 하기 위해서 reverse_lazy를 사용합니다.
# template_name = "blog/내가_원하는_파일명.html" # 기본값: blog/post_confirm_delete.html
class PostTest(CreateView):
model = Post
# 이렇게 재정의 하는 것을? 매서드 오버라이딩이라고 합니다.
def get(self, request):
return HttpResponse("get 요청이 왔습니다.")
def post(self, request):
return HttpResponse("post 요청이 왔습니다.")
FBV와는 조금 다르게 선언한다.
기존의 FBV의 urls.py 선언은 다음과 같다.
from django.urls import path
from . import views
urlpatterns = [
path("", views.blog_list, name="blog_list"),
path("<int:pk>/", views.blog_details, name="blog_details"),
path("write/", views.blog_write, name="blog_write"),
path("edit/<int:pk>/", views.blog_edit, name="blog_edit"),
path("delete/<int:pk>/", views.blog_delete, name="blog_delete"),
path("test/", views.test, name="test"),
]
그렇지만 CBV의 urls.py 선언은 다음과 같다.
from django.urls import path
from . import views
urlpatterns = [
path("", PostList.as_view()),
path("<int:pk>/", PostDetail.as_view()),
path("write/", PostCreate.as_view()),
path("edit/<int:pk>/", PostUpdate.as_view()),
path("delete/<int:pk>/", PostDelete.as_view()),
path("test/", PostTest.as_view()),
]
여기서 제네릭 뷰(클래스 뷰)의 템플릿 형식에 대해 짚고 넘어갈 필요성이 있다.
PostList (ListView)
{% for post in object_list %}
{{ post.title }}
{% endfor %}
PostDetail (DetailView)
{{ object.title }}
PostCreate (CreateView)
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Create</button>
</form>
PostUpdate (UpdateView)
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Update</button>
</form>
PostDelete (DeleteView)
<form method="post">
{% csrf_token %}
Are you sure you want to delete "{{ object.title }}"?
<button type="submit">Delete</button>
</form>
<h2>Post List</h2>
<ul>
{% for post in object_list %}
<li>
<a href="{% url 'blog_details' post.pk %}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
<h2>{{ object.title }}</h2>
<p>{{ object.content }}</p>
<h2>Create / Update</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">저장</button>
</form>
<h2>Delete</h2>
<form method="post">
{% csrf_token %}
<p>"{{ object.title }}"을 정말로 삭제하시겠습니까?</p>
<a href="{% url 'blog_details' object.pk %}">취소</a>
<button type="submit">삭제</button>
</form>
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
head_image = models.ImageField(upload_to="blog/images/%Y/%m/%d/", blank=True)
file_upload = models.FileField(upload_to="blog/files/%Y/%m/%d/", blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
def __str__(self):
return self.title
# blog > admin.py
from django.contrib import admin
from .models import Post
admin.site.register(Post)
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
# settings.py
STATIC_URL = "static/"
STATICFILES_DIRS = [BASE_DIR / "static"]
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"
# 프로젝트명 > urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path("admin/", admin.site.urls),
path("blog/", include("blog.urls")),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# post_list.html
<h2>Post List</h2>
<form action="" method="get">
<input name="q" type="text">
<button type="submit">검색</button>
</form>
<ul>
{% for post in object_list %}
<li>
<a href="{% url 'blog_details' post.pk %}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
1이라고 검색을 한 결과이다. 1이라는 제목을 가진 게시물이 검색되었다.
기본적으로 Django의 ListView는 모델의 모든 객체를 가져와서 리스트로 보여준다. 하지만, 특정 조건을 만족하는 객체만 보여주거나, 객체를 정렬하는 등의 커스텀 동작을 원할 경우 get_queryset
메서드를 재정의해서 원하는 동작을 구현해야 한다!
class PostList(ListView):
model = Post
ordering = "-pk"
def get_queryset(self):
queryset = super().get_queryset()
# request에서 GET 파라미터 q를 가져옴
q = self.request.GET.get("q", "")
if q:
queryset = queryset.filter(
Q(title__icontains=q) | Q(content__icontains=q)
).distinct()
return queryset
위 코드에서는 사용자의 검색어가 제목이나 내용에 포함된 Post 객체만 필터링하여 가져오도록 선언되었다.
차례대로 1과 2를 검색한 반환값이다.
Base views
View : 최상위 제네릭 뷰, 기본 뷰, django view를 만드는데 필요한 기능 제공
TemplateView : 템플릿이 주어지면 렌더링을 해주는 뷰
RedirectView : URL이 주어지면 리다이렉트 해주는 뷰
Generic display views
DetailView : model과 템플릿 받아 조건에 맞는 상세 오브젝트를 보여줍니다.
ListView : model과 템플릿 받아 전체 오브젝트를 보여줍니다.
Generic editing views
FormView : 폼을 보여주고 처리합니다.
CreateView : 폼을 보여주고 객체를 생성합니다.
UpdateView : 폼을 조건에 맞게 보여주고 객체를 수정합니다.
DeleteView : 객체를 삭제합니다.
Generic date views
ArchiveIndexView : 조건에 맞는 객체의 날짜 정보를 출력합니다.
YearArchiveView : 연도에 맞는 객체를 출력합니다.
MonthArchiveView : 월에 맞는 객체를 출력합니다.
WeekArchiveView : 주에 맞는 객체를 출력합니다.
DayArchiveView : 일에 맞는 객체를 출력합니다.
TodayArchiveView : 오늘 날짜에 객체를 출력합니다.
DateDetailView : 연, 월, 일 조건에 맞는 객체를 출력합니다.
# blog > admin.py
from django.contrib import admin
from .models import Post
class PostAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'content', 'created_at', 'updated_at']
# fields = ['title', 'content'] # 이전 버전에서는 fields를 사용했습니다.
admin.site.register(Post, PostAdmin)
관리자 페이지에서 각 모델의 데이터를 어떻게 보여줄지, 어떤 필드를 보여줄지, 어떤 필드를 수정할 수 있게 할지 등을 설정해줄 필요가 있다.
위 코드는 PostAdmin
클래스에서 list_display
를 통해 관리자 사이트에서 Post
객체의 목록을 보여줄 때 어떤 필드들을 보여줄 것인지를 설정하고 있다.
또한, admin.site.register(Post, PostAdmin)
을 통해 Post 모델과 PostAdmin
클래스를 관리자 사이트에 등록함으로써, 관리자 사이트에서는 PostAdmin
클래스에 정의된 설정대로 Post
객체를 보여주게 된다.