갈루아의 반서재


Haystack 설치 및 실행하기
Getting Started with Haystack



실습용으로 아래와 같이 간단한 노트 앱에 검색 기능을 붙여보자.


myapp/models.py

1
2
3
4
5
6
7
8
9
10
11
from django.contrib.auth.models import User
from django.db import models
 
class Note(models.Model):
    userid = models.ForeignKey(User, related_name='user_note')
    pub_date = models.DateTimeField()
    title = models.CharField(max_length=200)
    body = models.TextField()
 
    def __unicode__(self):
        return self.title
cs



1
2
3
4
5
6
7
8
9
10
# python manage.py makemigrations
Migrations for 'vikander':
  0007_note.py:
    - Create model Note
# python manage.py migrate
Operations to perform:
  Apply all migrations: auth, sessions, admin, avatar, contenttypes, vikander
Running migrations:
  Rendering model states... DONE
  Applying vikander.0007_note... OK
cs



Installation



1
# pip install django-haystack
cs



Configuration


설정파일(settings.py) 내 INSTALLED_APPS 에 haystack 을 추가한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Application definition
 
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'haystack',
    'vikander',
    'avatar',
    'django.contrib.humanize',
]
 
cs



사용하는 백엔드의 종류가 알맞게 HAYSTACK_CONNECTION 추가한다. 본 포스팅에서는 엘라스틱서치의 경우이며, 다른 에제는 http://django-haystack.readthedocs.io/en/v2.5.1/tutorial.html#modify-your-settings-py 를 참조한다.


백엔드가 설치되어 있지 않는 경우에는 다음 링크를 참고하여 설치한다.

http://django-haystack.readthedocs.io/en/v2.5.1/installing_search_engines.html



1
2
3
4
5
6
7
HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE''haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
        'URL''http://127.0.0.1:9200/',
        'INDEX_NAME''haystack',
    },
}
cs




Handling data


서로 다른 모델 간에 하나의 SearchIndex 를 사용할 수도 있고, 인덱싱하고자하는 각각의 모델 타입별로 SearchIndex 를 만들 수도 있다.


SearchIndex 를 생성하기 위해서는 데이터를 저장하고자하는 필드와 get_model method 를 정의하기 위해 indexes.SearchIndex 와 indexes.Indexable 로 나누는 것이다.


앞서 만든 Note 모델에 대응하는 NoteIndex 를 아래와 같이 생성한다. 일반적으로 search_indexes.py 파일로 만들지만 꼭 그래야하는 것은 아니다.


search_indexes.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import datetime
from haystack import indexes
from .models import Note, User
 
class NoteIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True)
    title = indexes.CharField(model_attr='title')
    body = indexes.CharField(model_attr='body')
    author = indexes.CharField(model_attr='userid')
    pub_date = indexes.DateTimeField(model_attr='pub_date')
 
    def get_model(self):
        return Note
 
    def index_queryset(self, using=None):
        """Used when the entire index for model is updated."""
        return self.get_model().objects.filter(pub_date__lte=datetime.datetime.now())
cs

모든 SearchIndex 는 document=True 라는 하나의 필드를 필요로 한다. 이는 haystack 와 검색엔진으로 하여금 어느 필드가 primary 인지를 알려준다.


추가적으로 텍스트 필드에 use_template=True 값을 제공하는데, 이는 검색엔진이 인덱싱하는 문서를 생성하는 데이터 템플릿을 사용하도록 해준다.  templates/search/indexes/<myapp>/note_text.txt 라는 템플릿 디렉토리 내에 새로운 템플릿을 생성할 필요가 있다. 디렉토리의 위치 및 들어갈 코드는 아래와 같다.


1
2
3
4
5
6
7
 
├── templates
│   ├── search
│   │   └── indexes
│   │       └── vikander
│   │           └── note_text.txt
 

cs


1
2
3
{{ object.title }}
{{ object.userid.get_full_name }}
{{ object.body }}
cs



Setting Up The Views


URLconf 에 아래와 같이 뷰를 추가한다.

settings.py

1
2
3
4
5
urlpatterns = [
 
    url(r'^search/', include('haystack.urls')),
 
]
cs



검색 템플릿을 만든다.


search/search.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<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>
cs




Reindex


이제 남은 것은 데이터베이스의 데이터를 검색 인덱스로 집어넣는 단계이다. 아래와 같은 에러가 발생하는 경우가 있다.


1
2
3
# python manage.py rebuild_index
raise MissingDependency("The 'elasticsearch' backend requires the installation of 'elasticsearch'. Please refer to the documentation.")
haystack.exceptions.MissingDependency: The 'elasticsearch' backend requires the installation of 'elasticsearch'. Please refer to the documentation.
cs



이와 관련하여 아래 링크가 도움이 된다.


Error: The 'elasticsearch' backend requires the installation of 'requests'. How do I fix it?

http://stackoverflow.com/questions/22412045/error-the-elasticsearch-backend-requires-the-installation-of-requests-how


Can't get Elasticsearch working with Django

http://stackoverflow.com/questions/28257502/cant-get-elasticsearch-working-with-django



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# pip install pyelasticsearch
Collecting pyelasticsearch
  Downloading pyelasticsearch-1.4.tar.gz (53kB)
    100|████████████████████████████████| 61kB 769kB/s
Collecting certifi (from pyelasticsearch)
  Using cached certifi-2016.9.26-py2.py3-none-any.whl
Collecting elasticsearch<2.0.0,>=1.3.0 (from pyelasticsearch)
  Downloading elasticsearch-1.9.0-py2.py3-none-any.whl (59kB)
    100|████████████████████████████████| 61kB 4.5MB/s
Collecting urllib3<2.0,>=1.8 (from pyelasticsearch)
  Downloading urllib3-1.19.1-py2.py3-none-any.whl (104kB)
    100|████████████████████████████████| 112kB 4.7MB/s
Collecting simplejson>=3.0 (from pyelasticsearch)
  Downloading simplejson-3.10.0.tar.gz (77kB)
    100|████████████████████████████████| 81kB 3.0MB/s
Requirement already satisfied: six<2.0,>=1.4.0 in /root/anaconda/envs/envalicia/lib/python3.5/site-packages (from pyelasticsearch)
Building wheels for collected packages: pyelasticsearch, simplejson
  Running setup.py bdist_wheel for pyelasticsearch ... done
  Stored in directory: /root/.cache/pip/wheels/1b/0c/0e/65b564e99a54d8db71e27f3997e55a27a2ab74960f001dac01
  Running setup.py bdist_wheel for simplejson ... done
  Stored in directory: /root/.cache/pip/wheels/43/c5/ef/edcebbb19becffd2ba75bf219afdbb4ca85198b2d909f1b31b
Successfully built pyelasticsearch simplejson
Installing collected packages: certifi, urllib3, elasticsearch, simplejson, pyelasticsearch
Successfully installed certifi-2016.9.26 elasticsearch-1.9.0 pyelasticsearch-1.4 simplejson-3.10.0 urllib3-1.19.1
cs



1
2
3
4
5
6
7
8
# 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 0 notes
cs



정상적으로 리인덱스를 마쳤다.

실제 화면은 다음과 같다.













http://django-haystack.readthedocs.io/en/v2.5.0/tutorial.html#installation