전체 페이지뷰

2017년 1월 14일 토요일

Blog 검색: Q class

김석훈 님의 저서 '파이썬 웹 프로그래밍: 실전편"을 토대로 공부를 진행하고 있습니다.
UI 등의 그림 설명이 잘 나와있으므로 저같은 초보를 위해서는 아주 좋은 교재인듯 합니다.
이번에는 블로그앱에 검색 기능을 추가해 봅니다.
장고 내장 기능이 Q 객체를 이용합니다. 이름이 의미하듯이 Query 처리를 위한 클래스입니다.

UI 설계

search를 누르면 블로그를 검색하는 폼을 보여주고, submit을 누르면 결과도 역시 같은 페이지에 출력할 겁니다.

테이블 변경은 없습니다.

URL설계

검색 폼을 위한 url이 하나 추가됩니다.
/blog/search/
뷰의 이름은 SearchFormView(FormView)
템플릿은 post-search.html로 작성합니다.

코딩

URLconf 코딩

기존의 blog/urls.py에 url을 추가합니다.

(myvenv) D:\myDjango\blog>subl urls.py
  # Example: /tag/tagname/
    url(r'^tag/(?P<tag>[^/]+(?u))/$', PostTOL.as_view(), name='tagged_object_list'),
    # 추가
    # Example: /search/
    url (r'^search/$', SearchFormView.as_view(), name='search'),
    ]
cs


뷰 코딩

뷰를 만들기 위해서는 먼저 검색 폼이 지정되어야 합니다.
따라서 forms.py 파일부터 먼저 만들겠습니다.

(myvenv) D:\myDjango\blog>subl forms.py
from django import forms
class PostSearchForm(forms.Form):
    search_word = forms.CharField(label='Search Word')
cs

forms 모듈을 임포트하고, django.forms 모듈의 Form 클래스를 상속받습니다.
'Search Word'라는 라벨을 붙인 CharField를 지정해줍니다.

이제 views.pySearchFormView를 추가하겠습니다.

from django.views.generic import ListView, DetailView, TemplateView  
from django.views.generic.dates import ArchiveIndexView, YearArchiveView, MonthArchiveView
from django.views.generic.dates import DayArchiveView, TodayArchiveView
from blog.models import Post
from tagging.models import Tag, TaggedItem 
from tagging.views import TaggedObjectList 
from django.views.generic.edit import FormView  #추가
from blog.forms import PostSearchForm           #추가
from django.db.models import Q                  #추가
from django.shortcuts import render             #추가
# 중략
class PostTAV(TodayArchiveView):
    model = Post
    date_field = 'modify_date'
# 상단 동일
#    FormView 추가
class SearchFormView(FormView):
    form_class = PostSearchForm
    template_name = 'blog/post_search.html'
    def form_valid(self, form):
        schWord = '%s' % self.request.POST['search_word']
        post_list = Post.objects.filter(Q(title__icontains=schWord) | 
            Q(description__icontains=schWord) | 
            Q(content__icontains=schWord)).distinct()
        context = {}
        context['forms'= form
        context['search_term'= schWord
        context['object_list'= post_list
        return render(self.request, self.template_name, context)
cs

프로젝트가 길어짐에 따라 점점 소스가 길어집니다.

네줄의 임포트 구문과, FormView를 위한 클래스 하나를 추가했습니다.
FormView 제너릭뷰와 앞에서 작성한 PostSearchForm, 그리고 검색에 팔요한 Q 클래스와 처리에 필요한 render() 함수를 임포트 합니다.

SearchFormView는 FormView를 상속받습니다. 그리고 그 안에 폼으로 사용할 클래스를 PostSearchForm으로 지정해 주고, 템플릿을 지정합니다.
이 클래스 안에는 쿼리를 처리할 form_valid라는 함수가 하나 들어 있습니다.
검색어를 Q 객체로 항목에 따라 검색해서 filter에 넘겨주어 post_list에 저장합니다.
그리고 컨텍스트 변수를 하나 Dictionary형으로 생성해서 각각 form, schWord, post_list를 저장합니다.

그리고 마지막으로 render 함수를 이용해서 최종적인 HttpResponse객체를 반환하게 됩니다.

템플릿 코딩

기존의 search 메뉴에 링크를 연결하기 위해 base.html을 수정합니다.

(myvenv) D:\myDjango\templates>subl base.html
    <li><a href="{% url 'blog:post_archive' %}">Archive</a></li>
    <li><a href="{% url 'blog:search' %}">Search</a></li<!--수정-->
    <li><a href="{% url 'admin:index' %}">Admin</a></li>
cs

그리고 post_search.html을 작성합니다.

(myvenv) D:\myDjango\blog\templates\blog>subl post_search.html

{% extends "base.html" %}
{% block title %}post_search.html{% endblock %}
{% block content %}
<div id="content">
<h1>Blog Search</h1>
<form action="." method="post"> {% csrf_token %}
    {{ form.as_table }}
    <input type="submit" value="Submit" />
</form>
<br/><br/>
{% if object_list %}
{% for post in object_list %}
    <h2><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h2>
    {{ post.modify_date|date:"N d, Y" }}
    <p>{{ post.description }}</p>
{% endfor %}
{% elif search_term %}
<b><i>Search Word({{ search_term }}) Not Found !</i></b>
{% endif %}
</div>
{% endblock %}
cs

검색 폼을 출력하고 csrf 공격(크로스 사이트 요청 위조 공격)을 방지하기 위해 token을 설정합니다. submit버튼을 누르면 URL로 요청이 전송됩니다.

검색과 일치하는 object_list가 존재하면 검색결과를 순회하면서 출력해줍니다.

이제, 작성이 모두 끝났으니 확인해봅니다.
메뉴의 'Search'를 누르면
서치 폼이 나타납니다.

검색도 잘 수행합니다.

댓글 없음:

댓글 쓰기