Django

Django 실습(7) 클래스 기반 뷰 (Class-Based Views) 이용하기

UserDonghu 2023. 10. 16. 18:41

그 전까지는 함수 기반 뷰를 이용해서 views.py를 만들었지만, 이제는 클래스 기반 뷰를 통해서 좀 더 쉽게 만들어보자.

generic 클래스에 대한 


실습

이제는 각 프로젝트마다 venv를 만들지않고 하나의 가상환경을 계속해서 쓰자.

 

pip freeze > requirements.txt 를 이용해서 버전관리를 할 수 있다.

설치할때는 pip install -r requirements.txt 사용

 

그리고 .gitignore파일을 만들고 venv를 입력해서 가상환경은 무시하도록 한다.

 

startproject를 하고, startapp으로 blog를 만들어서 클래스 기반 뷰를 사용해보자.

 

tutorialdjango / 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)

 

settings.py 추가

맨 마지막 줄에 MEDIA_ROOT와 MEDIA_URL 추가

MEDIA_ROOT = BASE_DIR / 'media'
MEDIA_URL = '/media/'

 

blog / urls.py

from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
    path('', views.postlist, name='postlist'), # 리스트
    path('<int:pk>/', views.postdetail, name='postdetail'), # 디테일
    path('write/', views.write, name='write'), # 생성
    path('edit/<int:pk>/', views.edit, name='edit'), # 업데이트
    path('delete/<int:pk>/', views.delete, name='delete'), # 삭제
]

 

blog / models.py

저장 후 python manage.py makemigrations, migrate 꼭 하기

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 / views.py

Django에 내장하고있는 generic 클래스들을 이용해서 CRUD를 구현한다.

from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from .models import Post
from django.urls import reverse_lazy

# 클래스 상속받아서 생성
class PostList(ListView):
    model = Post # models.py의 Post를 모델로 하겠다.
    ordering = '-pk' # 게시글 순서를 최신글이 위로오게 변경
    # template_name = 'blog/변경.html' # template은 이런식으로 변경

class PostDetail(DetailView):
    model = Post

class PostCreate(CreateView):
    model = Post
    fields = '__all__' # 입력할 필드를 설정 ex) ['title', 'content']
    success_url = reverse_lazy('blog:postlist') # reverse('postlist')와 차이점은 lazy는 지연을 줘서 DB에 적용이 될 때 까지 기다린후 이동한다.

class PostUpdate(UpdateView):
    model = Post
    fields = '__all__'
    success_url = reverse_lazy('blog:postlist')

class PostDelete(DeleteView):
    model = Post
    success_url = reverse_lazy('blog:postlist')
    
# 변수에 할당
postlist = PostList.as_view() # as_view는 인스턴스를 생성하는 진입 메소드
postdetail = PostDetail.as_view()
write = PostCreate.as_view()
edit = PostUpdate.as_view()
delete = PostDelete.as_view()

 

blog / templates / blog 안에 html 파일 추가

 

Django에서 CBV(Class-Based Views)를 사용할 때, template_name을 따로 지정하지 않으면 템플릿 이름과 템플릿에서 접근 방법은 다음과 같은 규칙을 따른다.

 

ListView

템플릿 이름 : <app_name>/<model_name_소문자>_list.html

템플릿에서 접근 : {{ object_list }}

 

DetailView

템플릿 이름 : <app_name>/<model_name_소문자>_detail.html

템플릿에서 접근 : {{ object }}

 

CreateView

템플릿 이름 : <app_name>/<model_name_소문자>_form.html

템플릿에서 접근 : {{ form }}

 

UpdateView

템플릿 이름 : <app_name>/<model_name_소문자>_form.html

템플릿에서 접근 : {{ form }}

 

DeleteView

템플릿 이름 : <app_name>/<model_name_소문자>_confirm_delete.html

템플릿에서 접근 : {{ object }}

 

blog / post_list.html

<h2>Post List</h2>
<ul>
    {% for post in object_list %}
    <li>
        <p><a href="{% url 'blog:postdetail' post.pk %}">{{ post.title }}</a></p>
        <p>{{ post.content }}</p>
    </li>
    {% endfor %}
</ul>

 

blog / post_detail.html

<h2>{{ object.title }}</h2>
<p>{{ object.content }}</p>
<a href="{% url 'blog:edit' object.pk %}">수정</a>
<a href="{% url 'blog:delete' object.pk %}">삭제</a>

 

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

<form method="post">
    {% csrf_token %}
    <p>"{{ object.title }}" 글을 정말 삭제하시겠습니까?</p>
    <p><a href="{% url 'blog:postdetail' object.pk %}">취소</a></p>
    <button type="submit">네</button>
</form>

검색 기능 추가

 

blog / post_list.html

<h2>Post List</h2>
<form action="" method="get"> # 검색 폼 추가
    <input name="q" type="search">
    <button type="submit">검색</button>
</form>
<ul>
    {% for post in object_list %}
    <li>
        <p><a href="{% url 'blog:postdetail' post.pk %}">{{ post.title }}</a></p>
        <p>{{ post.content }}</p>
    </li>
    {% endfor %}
</ul>

 

blog / views.py 수정

from django.db.models import Q # 위에 추가

class PostList(ListView):
    model = Post
    ordering = '-pk'

    def get_queryset(self): # get요청 받으면 쿼리셋을 리턴
        queryset = super().get_queryset() # 상속받는 ListView의 get_queryset 메서드 사용 (전체 반환)

        # request의 GET 파라미터에서 'q'를 가져옴
        q = self.request.GET.get('q', '')

        # 'q' 파라미터가 있으면, 필터링
        if q:
            queryset = queryset.filter(Q(title__icontains=q) | Q(content__icontains=q))
        return queryset

Admin 페이지의 Post 관리페이지에서 보이는거 수정하기

python manage.py createsuperuser 로 admin생성

 

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'] # Post 관리페이지에서 보이게 할거

admin.site.register(Post, PostAdmin)

 

그럼 이렇게 Post에 대한 정보들이 다 나온다