장고:검색기능

학교의 모든 지식. SMwiki
둘러보기로 이동 검색으로 이동

장고! 웹 프레임워크! 틀:장고

  1. 장고:개요
  2. 장고:웹페이지설계
    1. 장고:앱
    2. 장고:url
    3. 장고:model
      1. 장고:DB
      2. 장고:모델 필드
      3. 장고:모델의 변경
    4. 장고:view
      1. 장고:클래스형 뷰, 제네릭 뷰
      2. 장고:view 각종 기능
    5. 장고:template
    6. 장고:static. 정적파일 사용하기
      1. 장고:CSS 사용하기
      2. 장고:JS 사용하기
      3. 장고:글꼴 사용
      4. 장고:부트스트랩
    7. 장고:media. 미디어 파일 사용하기
  3. 장고:관리자페이지
  4. 장고:settings.py
  5. 장고:기능구현
    1. 장고:회원관리
    2. 장고:유저
    3. 장고:커스텀 유저
    4. 장고:소셜로그인
    5. 장고:입력받기
    6. 장고:저장된 내용 활용하기
    7. 장고:변수 내보내기
    8. 장고:글쓰기
    9. 장고:페이징(페이지나누기)
    10. 장고:답변쓰기(댓글쓰기)
    11. 장고:추천,즐겨찾기
    12. 장고:새글(최신글) 나타내기
    13. 장고:썸머노트 설치
    14. 장고:네비게이션 바 만들기
    15. 장고:검색기능
    16. 장고:카테고리 만들기
    17. 장고:사진 올리기
    18. 장고:파일 업로드
    19. 장고:이메일app 만들기
    20. 장고:매직 그리드
  6. 장고:웹서비스
    1. 장고:Git
    2. 장고:리눅스에 올리기
    3. 장고:우분투에 올리기(nginx 사용)
    4. 장고:도커로 올리기
  7. 장고:팁

개요[편집 | 원본 편집]

검색기능은 DB에 쿼리하는 방식으로 이루어진다.

view 수정[편집 | 원본 편집]

목록을 조회하는 view를 수정한다.

다음과 같은 형태로 추가.

from django.db.models import Q  # 검색을 위함. filter에서 OR조건으로 조회하기 위한 함수.(장고제공)

def list(request):
    question_list = Question.objects.order_by('-create_date')  # 일단 객체 목록을 받아온다.
    keyword = request.GET.get('keyword', '')  # 검색어.  ''는 왜 필요할까? 없어도 되나?
    if keyword:  # 검색어가 있다면
        result = []  # 검색결과를 담기 위한 리스트를 만든다.
        keywords = keyword.split(' ')  # 공백이 있는 경우 나눈다.
        for kw in keywords:  # 띄어쓰기로 검색을 하는 경우가 많으니까, 다 찾아줘야지.
            result += question_list.filter(  # 검색해서 검색결과에 더한다.
            Q(subject__icontains=kw) |  # 제목검색
            Q(content__icontains=kw) |  # 내용검색
            Q(author__username__icontains=kw) |  # # question모델의 상위인 user모델의 username에서 검색.
            Q(answer__author__username__icontains=kw)  # 하위모델인 answer모델의 참조인 user모델의 username에서 검색.
            ).distinct()  # 중복된 내용이 있으면 제거하는 함수.
        question_list = result  # 모은 결과를 리스트에 담는다.
        
    .
    .
    .
    context = {  # keyword를 추가하여 반환해주자. 검색창에 검색어가 담겨 있게끔.
           'keyword': keyword,  # 검색어는 다시 돌려준다.
  • POST방식이 아니라 GET으로 받아오는 것은 뒤로가기 버튼을 누를 때 '만료된 페이지입니다'라는 문구가 뜨기 때문이다. 그럼 다시 찾아들어가야 하고.. 불편해.
  • ('만료된 페이지입니다' 문구는 POST요청 시 중복요청을 방지하기 위해 발생시키는 오류이다.)
  • subject__contains 대신 subject__icontains 형태를 사용하는 건 i를 붙였을 때 대소문자 구분없이 찾아주기 때문이다.

탬플릿 수정[편집 | 원본 편집]

검색창을 넣어야 하는 목록 탬플릿에 다음과 같이 추가한다.

<form id="searchForm" method="get" action="{% url 'pool:list' %}">
    <div class="input-group mb-3">
        <input type="text" class="form-control" name="keyword" placeholder="여따 검색하쇼" value="{{ keyword|default_if_none:'' }}">
        <div class="input-group-append">
        <input class="btn btn-outline-secondary" type="submit" id="btn_search" value="검색"></input>
    </form>
    </div>
</div>

현재 페이지를 그대로 불러올 경우, url 대신 #앵커 형태로 넣을 수도 있다.

정렬기능[편집 | 원본 편집]

검색 뿐 아니라 날짜 순, 댓글 순, 추천 순 등으로 정렬해야 할 때가 있다.

탬플릿 수정[편집 | 원본 편집]

form을 이용해 정렬기준을 전달한다.

검색 버튼을 누를 때 함께 전달되게 하기 위해 검색창을 정의하는 div 안에 다음과 같은 형태로 넣어준다.

        <div class="col-2">
        <select class="form-control so" name="ordering">
        <option value="recent" {% if so == 'recent' %}selected{% endif %}>최신순</option>
        <option value="recommend" {% if so == 'recommend' %}selected{% endif %}>추천순</option>
        <option value="popular" {% if so == 'popular' %}selected{% endif %}>인기순</option>
        </select>

뷰 수정[편집 | 원본 편집]

다음과 같은 형태의 내용을 추가한다.

from django.db.models import Count  # annotate에서 DB에 해당하는 무언가를 셀 때 사용하는 함수.

def list(request):
    .
    .
    .
    # 정렬 기능
    ordering = request.GET.get('ordering', 'recent')  # 정렬기준
    if ordering == 'recent':  # 시간순 정렬
        question_list = Question.objects.order_by('-create_date')
    elif ordering == 'popular':  # 댓글순 정렬
        question_list = Question.objects.annotate(num_answer=Count('answer')).order_by('-num_answer', '-create_date')
    elif ordering == 'recommend':  # 추천순 정렬
        question_list = Question.objects.annotate(num_voter=Count('voter')).order_by('-num_voter', '-create_date')
    else:  # recent
        pass
    .
    .
    .
    context = {  # ordering을 추가하여 반환해주자. 검색창에 검색어가 담겨 있게끔.
           'ordering': ordering,  # 선택한 정렬순서는 다시 돌려준다.

정렬기능에서 모델객체를 불러오기 때문에 검색기능 위에 두어야 한다.


페이징[편집 | 원본 편집]

그런데, 검색을 하고 나면... 페이징기능이 제대로 되먹지가 않는다; 검색에서 사용한 다양한 변수들이 전달되지 않기 때문인데, 2가지 선택지가 있다.

  1. 페이징 버튼을 form으로 바꾸어 hidden으로 각 변수들을 보내기.
  2. JS를 이용하여 페이징버튼이 눌릴 때 form 안의 내용도 함께 제출되게끔 하기.

페이징버튼마다 form을 만드는 건 굉장히 번거롭고, form 안의 내용이 바뀔 때마다 페이징 버튼에 대해 일일이 바꿔주어야 하는 불편함이 있다. 때문에 2번 방법을 사용한다.

탬플릿 수정[편집 | 원본 편집]

폼에 page정보 추가[편집 | 원본 편집]

멀리 떨어진 form을 제출해야 하는데, 나는 그 방법은 모르겠다;; 하여, 기존 form 안에 다음을 추가한다.(page정보를 form에 담아 보내기 위해)

<input type="hidden" id="page" name="page" value="{{ page }}">

페이지버튼 변형하기[편집 | 원본 편집]

자바스크립트에서 작동시키기 위해 페이지버튼의 구성을 바꾼다.

위의 형태에서 아래의 형태로.

<a class="page-link" href="?page={{ page_number }}">{{ page_number }}</a>

<a class="page-link" data-page="{{ page_number }}" href="#">{{ page_number }}</a>


자바스크립트[편집 | 원본 편집]

페이지 정보도 같이 제출되게끔 페이지정보를 form 안에 hidden 필드로 넣고 진행한다.

<!--페이지 버튼을 누르면 페이지 정보와 함께 검색폼의 내용도 제출되게끔. searchForm 안의 hidden으로 page를 넣는다.-->
<script type='text/javascript'>
$(document).ready(function(){
    $(".page-link").on('click', function() {    // page-link 클래스를 가진 버튼이 클릭되면
        $("#page").val($(this).data("page"));   // 해당 값을 page에 담고.
        $("#searchForm").submit();              // searchForm 안의 페이지에 담아 제출한다.
    });

// 검색버튼 클릭할 경우, 페이지번호를 받지 못하므로, 값을 넣어주어야 한다.
    $("#btn_search").on('click', function() {
        $("#page").val(1);  // 검색버튼을 클릭할 경우 1페이지부터 조회한다.
        $("#searchForm").submit();
    });
});
</script>