3. 튜토리얼 따라하기 - 설문조사 - 3

2019. 5. 28. 17:10파이썬 웹프로그래밍

3.14. 제네릭 뷰 사용하기

제네릭 뷰는 장고에서 미리 준비한 뷰를 말합니다. 웹 프로그래밍에 일반적으로 사용되는 뷰들은 이미 장고에서 대부분 만들어 뒀습니다. 이것들을 사용하면 코드를 적게 사용하고 기능들을 빠르게 완성할 수 있습니다.

다음 코드를 참조하여 views.py를 수정합니다. 기존에 있던 index, detail, results 뷰는 삭제합니다.

from django.views import generic
from .models import Question, Choice

class IndexView(generic.ListView):
    template_name='polls/index.html'
    context_object_name='latest_question_list'

    def get_queryset(self):
        """Return the last five published question."""
        return Question.objects.order_by('-pub_date')[:5]

class DetailView(generic.DetailView):
    model=Question
    template_name='polls/detail.html'

class ResultsView(generic.DetailView):
    model=Question
    template_name='polls/results.html'

새로 추가한 뷰는 기존의 함수형 뷰와 달리 클래스형 뷰입니다. 클래스형 뷰로 바꾸면서 장고가 만들어둔 제네릭 뷰중에 한가지씩을 상속받아 적용했습니다. 코드가 훨씬 간결해지는 것을 확인 할 수 있습니다.

클래스형 뷰로 변경하면 urls.py도 수정해줘야 합니다.

urlpatterns=[
    path('', views.IndexView.as_view(), name='index'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
    path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
    path('<int:question_id/vote/', views.vote, name='vote'),
]

urls.py에 있는 urlpatterns 변수의 내용을 살펴 봅시다. path 함수는 그대로 사용하고 route 패턴도 동일합니다. 바뀐 부분은 route 패턴에 들어있는 패턴이름과 view 인자입니다. 패턴 이름을 question_id에서 pk로 변경했습니다. 그리고 뷰 이름을 바꾸고 뒤에 추가 코드를 붙였습니다. 함수형 뷰를 그대로 써주면 되지만 클래스형 뷰를 사용할 때는 꼭 뒤에 [as_view()]를 붙여야 하기 때문입니다. 꼭 기억해두시기 바랍니다.

3.15. 정적(Static) 파일 사용하기

정적 파일은 css나 js같은 파일을 의미합니다. 이런 파일을 사용하려면 static 폴더를 만들고 그 안에 파일을 저장한 후 사용할 수 있습니다.

우선 polls 폴더 밑에 static 폴더를 만들고 그 안에 polls 폴더를 하나 더 만들겠습니다. 그리고 style.css 파일을 추가합니다.

css 파일은 파이썬 파일이나 HTML과는 다르게 목록에 나오지 않습니다. [New -> File]을 선택합니다.

그리고 새 파일 창이 나타나면 [style.css]라고 파일이름을 입력하고 [OK] 버튼을 클릭합니다.

해당 파일에 다음 코드를 입력합니다.

body{
    background: white url("images/background.png") no-repeat;
    background-position: right bottom;
}

li a{
    color: green;
}

그리고 적절한 png 이미지를 찾아서 images 폴더를 생성하고 넣어 줍니다. 정확한 촐더 구조는 아래 그림을 참조 하시기 바합니다.

css 작성과 이미지 추가를 끝냈으면 css 파일을 불러와 사용할 수 있도록 index.html 파일을 수정하겠습니다. index.html 파일을 수정하겠습니다. index.html 파일의 head 태그 안에 다음 코드를 입력합니다.

{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}">

코드를 입력하면 아래 그림같은 모양이 됩니다.

이제 모든 기능의 추가가 끝났습니다. 제대로 동작하는지 확인하기 위해서 관리자 페이지 디능을 변경하겠습니다.

3.16. 관리자 폼 커스터마이징

기존에 관리자 페이지에서는 목혹 화면에 투표 제목만 나타나 있고 답변 목록도 수정할 수 없는 상황입니다. 그래서 투표 기능도 제대로 동작하지 않는 상태입니다. 이를 해결하기 위해 관리자 화면을 변경 해보겠습니다. 관리자 화면을 커스터마이징 하기 위해서는 admin.py에서 ModelAdmin을 상속받는 클래스를 만들고 register에 두 번째 인자로 전달해야 합니다.

class QuestionAdmin(admin.ModelAdmin):
    fieldsets=[
        (None, {'fields': ['question_text']}),
        ('Date information', {'fields': ['pub_date']}),
    ]
admin.site.register(Question, QuestionAdmin)

fieldsets 변수를 이용해 입력/수정 화면에서 각 항목들을 그룹화 하고 그룹의 이름까지 설정할 수 있습니다.

이번에는 답변 항목도 같이 등록하고 수정할 수 있도록 추가하겠습니다. Choice 모델도 임포트 합니다. 그리고 Choice 모델을 위한 옵션 클래스를 만드는 [StackedInline] 클래스를 상속 받습니다. 그리고 이 클래스를 [QuestionAdmin] 클래스에 [inlines] 클래스 변수에 추가 합니다.

from .models import Question, Choice

class ChoiceInline(admin.StackedInline):
    model=Choice
    extra=3

class QuestionAdmin(admin.ModelAdmin):
    fieldsets=[
        (None, {'fields': ['question_text']}),
        ('Date information', {'fields': ['pub_date']}),
    ]
    inlines=[ChoiceInline]

변경을 하면 답변 목록도 투표 입력/수정 화면에 나타납니다.

이제 답변을 편하게 추가할 수 있습니다. 아래 그림처럼 몇가지 답변을 입력 한 후 [SAVE] 버튼을 클릭합니다.

그럼 자동으로 답변이 추가 됩니다.

새로운 답변을 추가 한 후 다시 수정 화면으로 이동하면 이전과 다르게 각 답변이 있고 [Delete 체크 박스]도 나타나는 것을 확인 할 수 있습니다. 이렇게 입력이 편해졌습니다.

하지만 입력 화면이 아래로 많이 길어지기 때문에 불펴녀하다고 느껴집니다. 답변을 관리하는 형태를 바꿔서 화면을 좀 더 효율적으로 사용해 보겠습니다.

admin.py에 ChoiceInline 클래스의 부모 클래스를 StackedInline이 아니라 [TabularUnline]으로 변경합니다.

class ChoiceInline(admin.TabularInline):
    model=Choice
    extra=3

TabularInline은 인라인 아이템을 테이블 형식으로 보여줍니다.

3.17. 관리자 화면 목록 커스터마이징

관리자 화면의 폼을 커스터마이징 했으니 목록도 보기 좋게 바꾸겠습니다. 기존의 목록 화면은 아래 그림처럼 투표 질문만 보입니다. 질문 뿐 아니라 발행일 등 여러 항목이 나오고 다른 기능도 사용할 수 있도록 하나씩 변경 해봅시다.

목록에 보이는 항목을 변경하려면 admin.py에 list_display 클래스 변수를 추가해야 합니다. 변수의 값은 튜플로 출력하고 싶은 항목을 묶어서 설정합니다.

class QuestionAdmin(admin.ModelAdmin):
    fieldsets=[
        (None, {'fields': ['question_text']}),
        ('date information', {'fields': ['pub_date']}),
    ]

    list_display=('question_text','pub_date','was_published_recently')

    inlines=[ChoiceInline]

항목을 변경하면 목록에 바로 반영된 것을 확인할 수 있습니다. 특별히 모델의 필드뿐 아니라 메서드도 항목으로 출력할 수 있습니다. 하지만 출력되는 모양이 이쁘지 않으니 이것도 수정해 봅시다.

was_published_recently의 출력 모양을 변경하기 위해서는 모델 코드를 수정해야 합니다.

models.py에 was_publisehd_recently 메서드 아래쪽에 세가지의 값을 더 추가합니다.

class Question(models.Model):
    # ···

    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(day=1)

    was_published_recently.admin_order_field='pub_date'
    was_published_recently.boolean=True
    was_published_recently.short_description='published recently?'

admin_order_field : 원칙적으로 임의의 메서드에 의한 값은 정렬이 불가능 합니다. 대신 다른 값을 기준으로 정렬할 수 있는데 이 기준 항목을 설정하는 항목입니다.
boolean : 닶이 불리언 값 형태인지 설정합니다. True 설정하면 값 대신 아이콘이 나타납니다.
short_description : 항목의 헤더 이름을 설정합니다.

변경을 마치고 화면을 새로고침 해서 결과를 확인합니다.

변경 후 마지막 수정을 해보겠습니다. 관리자 페이지에 검색 기능과 필터 기능을 추가해 봅시다. 검색과 필터 기능은 admin.py에 QuestionAdmin 클래스에 list_filter, search_fields 클래스 변수를 추가하면 사용할 수 있습니다.

class QuestionAdmin(admin.ModelAdmin):
    fieldsets=[
        (None, {'fields': ['question_text]}),
        ('Date information', {'[fields': ['pub_date']}),
    ]

    list_display=('question_text', 'pub_date', 'was_published_recently')

    inlines=[ChoiceInline]

    list_filter=['pub_date']
    search_fields=['question_text']

관리자 페이지의 디자인을 변경하고 싶다면 django/contrib/admin/templates 의 템플릿을 복사해 프로젝트로 가져와 수정할 수 있습니다. 또 관리자 페이지를 커스터마이징에 대해 살펴볼 부분들이 아직 남아 있습니다. 하지만 튜토리얼에서 다루는 부분은 여기까지이기 때문에 다른 방법들은 이후의 과정을 통해 살펴보도록 하겠습니다.

튜토리얼을 따라하면서 장고로 웹 서비스를 만드는 전반적인 과정을 살펴봤습니다. 아직은 어렵게 느껴질 수 있습니다. 하지만 이후 과정에서 여러 가지 기능들을 만들어보면서 장고의 기능을 자세히 살펴볼 것이기 때문에 걱정하지 않으셔도 됩니다. 이제 실전 예제들을 만들어 봅시다.