[day-37] 인증 로직 구현, CBV

Joohyung Park·2024년 2월 27일
0

[모두연] 오름캠프

목록 보기
70/95

기획

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

인증 로직 구현

제네릭 뷰(CBV 선언 및 수정)

  • 제네릭 뷰 : 클래스 기반 뷰의 일종으로 Django에서 일반적인 패턴들을 구현해 놓은 클래스뷰
  • 클래스 뷰 : 클래스 형태로 만든 뷰

이러한 제네릭 뷰를 활용하여 쉽게 로직을 구현할 수 있다.

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 요청이 왔습니다.")

클래스 뷰의 urls.py 선언법

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)

    • 템플릿 이름 규칙: <appname>/<model_name소문자>_list.html
    • 기본 템플릿: <app_name>/post_list.html
    • 템플릿 접근 방법
    {% for post in object_list %}
        {{ post.title }}
    {% endfor %}
  • PostDetail (DetailView)

    • 템플릿 이름 규칙: <appname>/<model_name소문자>_detail.html
    • 기본 템플릿: <app_name>/post_detail.html
    • 템플릿 접근 방법
    {{ object.title }}
  • PostCreate (CreateView)

    • 템플릿 이름 규칙: <appname>/<model_name소문자>_form.html
    • 기본 템플릿: <app_name>/post_form.html
    • 템플릿 접근 방법
    <form method="post">
      {% csrf_token %}
      {{ form.as_p }}
      <button type="submit">Create</button>
    </form>
  • PostUpdate (UpdateView)

    • 템플릿 이름 규칙: <appname>/<model_name소문자>_form.html
    • 여기서의 기본 템플릿: <app_name>/post_form.html
    • 템플릿 접근 방법
    <form method="post">
      {% csrf_token %}
      {{ form.as_p }}
      <button type="submit">Update</button>
    </form>
  • PostDelete (DeleteView)

    • 템플릿 이름 규칙: <appname>/<model_name소문자>_confirm_delete.html
    • 기본 템플릿: <app_name>/post_confirm_delete.html
    • 템플릿 접근 방법
    <form method="post">
      {% csrf_token %}
      Are you sure you want to delete "{{ object.title }}"?
      <button type="submit">Delete</button>
    </form>

각 제네릭 뷰의 템플릿 선언

  • blog > post_list.html
<h2>Post List</h2>
<ul>
    {% for post in object_list %}
    <li>
        <a href="{% url 'blog_details' post.pk %}">{{ post.title }}</a>
    </li>
    {% endfor %}
</ul>

  • blog > post_detail.html
<h2>{{ object.title }}</h2>
<p>{{ object.content }}</p>

  • blog > post_form.html
<h2>Create / Update</h2>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">저장</button>
</form>

  • blog > post_confirm_delete.html
<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

모델과 admin 연결

# blog > admin.py

from django.contrib import admin
from .models import Post

admin.site.register(Post)

DB 연결

python manage.py makemigrations
python manage.py migrate

관리자 계정 생성

python manage.py createsuperuser

static, media 폴더 생성

# settings.py

STATIC_URL = "static/"
STATICFILES_DIRS = [BASE_DIR / "static"]

MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"

media 폴더 URL 선언

# 프로젝트명 > 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 :,, 일 조건에 맞는 객체를 출력합니다.

관리자 사이트의 Post 모델 시각화 설정

# 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 객체를 보여주게 된다.

profile
익숙해지기 위해 기록합니다

0개의 댓글