django haystack whoosh 검색 구현

2019-04-22

django로 만들고 있는 웹에 검색기능을 구현해봅시다. medium 포스팅이 이해하는데 정말 많은 도움이 되었습니다. 감사합니다.

공식문서 : https://django-haystack.readthedocs.io/en/master/tutorial.html

참고 : https://medium.com/@whj2013123218/%EC%9E%A5%EA%B3%A0-django-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-%EA%B2%80%EC%83%89%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-haystack%EA%B3%BC-whoosh%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EA%B8%B0%EC%B4%88-%EA%B2%80%EC%83%89%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84-19aa7f8040db

우선 검색할 내용의 기반이 되는 모델이 있어야합니다.

저는 제가 만들어 놓은 모델을 예제 소스로 가져왔습니다.

#posts/models.py
class Post(models.Model, HitCountMixin):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="posts")
    created_at = models.DateTimeField(auto_now=True)
    text = RichTextUploadingField()
    title = models.CharField(max_length=100)
    link = models.URLField()

설치

pip install django-haystack

pip install whoosh

그리고 settings.py에 아래와 같은 값을 추가해줍니다.

INSTALLED_APPS =[
    ...
    'haystack',
]
import os
...
WHOOSH_INDEX = os.path.join(BASE_DIR, 'whoosh_index')
HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
        'PATH': WHOOSH_INDEX,
    },
}

search_indexes.py 생성

import datetime
from haystack import indexes
from .models import Post

class NoteIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True, template_name='search/post_text.txt')
    author = indexes.CharField(model_attr='user')
    pub_date = indexes.DateTimeField(model_attr='created_at')

    def get_model(self):
        return Post

    def index_queryset(self, using=None):
        """Used when the entire index for model is updated."""
        return self.get_model().objects.all()

해당 models.py이 있는 폴더안에 search_indexes.py을 만들고 위의 코드를 붙여넣는다.

여기서 Post는 필자의 모델 이름이므로 본인의 모델 이름에 맞게 수정한다.

model_attr도 본인이 사용하고 있는 모델내의 필드 이름과 같아야 한다.

text에 있는 document=True는 한 필드에만 적용될 수 있고

이를 통해 Haystack과 검색 엔진이 text를 주요 필드로 삼아 검색하게 된다.

get_model에는 사용하고 싶은 모델의 이름을 리턴해주면 된다.

Post 모델을 검색하게 할 것이므로 Post를 리턴했다.

template 생성

use_template=Ture를 보면 알수있듯이 우리는 템플리는 사용한다고 했으므로 txt template을 하나 만들어야 한다. template_name에 지정한대로 폴더와 파일을 만들어주자. 공식문서에 나와있는 것처럼 이름은 모델명_필드명. txt로 하는것이 관리하기 편하다. 필자는 모델이 Post이고 document=True가 돼있는 필드는 text이므로 post.text.txt로 생성했다.

그리고 txt 파일안에 아래의 코드를 붙여 넣어주자

{{ object.title }}
{{ object.user.get_full_name }}
{{ object.text }}

여기서도 각 필드부분 즉 title, user, text는 본인의 모델에 맞게 변경해야 한다.

모델의 필드를 보고 참고해서 수정해주자.

필자의 디렉토리 구조는 아래와 같이 되어있다.

view를 위한 설정

urls.py에서 인식할 수 있게 코드를 추가한다.

path('search/', include('haystack.urls')),

Search Template을 만들자

방금 모델명_필드명.txt파일을 만든 search폴더 안에 search.html을 만든다.

아래의 코드를 붙여넣는다.

{% block content %}
    <h2>Search</h2>

    <form method="get" action=".">
        <table>
            {{ form.as_table }}
            <tr>
                <td>&nbsp;</td>
                <td>
                    <input type="submit" value="Search">
                </td>
            </tr>
        </table>

        {% if query %}
            <h3>Results</h3>

            {% for result in page.object_list %}
                <p>
                    <a href="{{ result.object.get_absolute_url }}">{{ result.object.title }}</a>
                </p>
            {% empty %}
                <p>No results found.</p>
            {% endfor %}

            {% if page.has_previous or page.has_next %}
                <div>
                    {% if page.has_previous %}<a href="?q={{ query }}&amp;page={{ page.previous_page_number }}">{% endif %}&laquo; Previous{% if page.has_previous %}</a>{% endif %}
                    |
                    {% if page.has_next %}<a href="?q={{ query }}&amp;page={{ page.next_page_number }}">{% endif %}Next &raquo;{% if page.has_next %}</a>{% endif %}
                </div>
            {% endif %}
        {% else %}
            {# Show some example queries to run, maybe query syntax, something else? #}
        {% endif %}
    </form>
{% endblock %}

그 후에 python manage.py rebuild_index 명령어를 쳐서 검색할 내용을 모아준다.

...\Virenv\Project\versio>python manage.py rebuild_index
WARNING: This will irreparably remove EVERYTHING from your search index in connection 'default'.
Your choices after this are to restore from backups or rebuild via the `rebuild_index` command.
Are you sure you wish to continue? [y/N] y
Removing all documents from your index because you said so.
All documents removed.
Indexing 3 posts

3 posts만 DB에 넣어놨으므로 성공하면 위와 같이 메세지가 뜬다.

http://localhost:8000/search/에 접속해서 검색을 해보자

검색결과가 알맞게 뜨면 성공이다.

190513 추가

자동 인덱싱이 안되는 줄 알았는데 영어로 검색해보니 쉽게 나왔다.

HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

settings,py에 한줄만 추가해주면 된다.