/bangkokhotel/lib/python2.5/site-packages/haystack/views.py
Python | 235 lines | 225 code | 5 blank | 5 comment | 0 complexity | 50a4907458326ee88c2bea3c65a67ab7 MD5 | raw file
1from django.conf import settings
2from django.core.paginator import Paginator, InvalidPage
3from django.http import Http404
4from django.shortcuts import render_to_response
5from django.template import RequestContext
6from haystack.forms import ModelSearchForm, FacetedSearchForm
7from haystack.query import EmptySearchQuerySet
8
9
10RESULTS_PER_PAGE = getattr(settings, 'HAYSTACK_SEARCH_RESULTS_PER_PAGE', 20)
11
12
13class SearchView(object):
14 __name__ = 'SearchView'
15 template = 'search/search.html'
16 extra_context = {}
17 query = ''
18 results = EmptySearchQuerySet()
19 request = None
20 form = None
21 results_per_page = RESULTS_PER_PAGE
22
23 def __init__(self, template=None, load_all=True, form_class=None, searchqueryset=None, context_class=RequestContext, results_per_page=None):
24 self.load_all = load_all
25 self.form_class = form_class
26 self.context_class = context_class
27 self.searchqueryset = searchqueryset
28
29 if form_class is None:
30 self.form_class = ModelSearchForm
31
32 if not results_per_page is None:
33 self.results_per_page = results_per_page
34
35 if template:
36 self.template = template
37
38 def __call__(self, request):
39 """
40 Generates the actual response to the search.
41
42 Relies on internal, overridable methods to construct the response.
43 """
44 self.request = request
45
46 self.form = self.build_form()
47 self.query = self.get_query()
48 self.results = self.get_results()
49
50 return self.create_response()
51
52 def build_form(self, form_kwargs=None):
53 """
54 Instantiates the form the class should use to process the search query.
55 """
56 data = None
57 kwargs = {
58 'load_all': self.load_all,
59 }
60 if form_kwargs:
61 kwargs.update(form_kwargs)
62
63 if len(self.request.GET):
64 data = self.request.GET
65
66 if self.searchqueryset is not None:
67 kwargs['searchqueryset'] = self.searchqueryset
68
69 return self.form_class(data, **kwargs)
70
71 def get_query(self):
72 """
73 Returns the query provided by the user.
74
75 Returns an empty string if the query is invalid.
76 """
77 if self.form.is_valid():
78 return self.form.cleaned_data['q']
79
80 return ''
81
82 def get_results(self):
83 """
84 Fetches the results via the form.
85
86 Returns an empty list if there's no query to search with.
87 """
88 return self.form.search()
89
90 def build_page(self):
91 """
92 Paginates the results appropriately.
93
94 In case someone does not want to use Django's built-in pagination, it
95 should be a simple matter to override this method to do what they would
96 like.
97 """
98
99 try:
100 page_no = int(self.request.GET.get('page', 1))
101 except (TypeError, ValueError):
102 raise Http404("Not a valid number for page.")
103
104 if page_no < 1:
105 raise Http404("Pages should be 1 or greater.")
106
107 start_offset = (page_no - 1) * self.results_per_page
108 self.results[start_offset:start_offset + self.results_per_page]
109
110 paginator = Paginator(self.results, self.results_per_page)
111
112 try:
113 page = paginator.page(page_no)
114 except InvalidPage:
115 raise Http404("No such page!")
116
117 return (paginator, page)
118
119 def extra_context(self):
120 """
121 Allows the addition of more context variables as needed.
122
123 Must return a dictionary.
124 """
125 return {}
126
127 def create_response(self):
128 """
129 Generates the actual HttpResponse to send back to the user.
130 """
131 (paginator, page) = self.build_page()
132
133 context = {
134 'query': self.query,
135 'form': self.form,
136 'page': page,
137 'paginator': paginator,
138 'suggestion': None,
139 }
140
141 if getattr(settings, 'HAYSTACK_INCLUDE_SPELLING', False):
142 context['suggestion'] = self.form.get_suggestion()
143
144 context.update(self.extra_context())
145 return render_to_response(self.template, context, context_instance=self.context_class(self.request))
146
147
148def search_view_factory(view_class=SearchView, *args, **kwargs):
149 def search_view(request):
150 return view_class(*args, **kwargs)(request)
151 return search_view
152
153
154class FacetedSearchView(SearchView):
155 __name__ = 'FacetedSearchView'
156
157 def __init__(self, *args, **kwargs):
158 # Needed to switch out the default form class.
159 if kwargs.get('form_class') is None:
160 kwargs['form_class'] = FacetedSearchForm
161
162 super(FacetedSearchView, self).__init__(*args, **kwargs)
163
164 def build_form(self, form_kwargs=None):
165 if form_kwargs is None:
166 form_kwargs = {}
167
168 # This way the form can always receive a list containing zero or more
169 # facet expressions:
170 form_kwargs['selected_facets'] = self.request.GET.getlist("selected_facets")
171
172 return super(FacetedSearchView, self).build_form(form_kwargs)
173
174 def extra_context(self):
175 extra = super(FacetedSearchView, self).extra_context()
176 extra['request'] = self.request
177 extra['facets'] = self.results.facet_counts()
178 return extra
179
180
181def basic_search(request, template='search/search.html', load_all=True, form_class=ModelSearchForm, searchqueryset=None, context_class=RequestContext, extra_context=None, results_per_page=None):
182 """
183 A more traditional view that also demonstrate an alternative
184 way to use Haystack.
185
186 Useful as an example of for basing heavily custom views off of.
187
188 Also has the benefit of thread-safety, which the ``SearchView`` class may
189 not be.
190
191 Template:: ``search/search.html``
192 Context::
193 * form
194 An instance of the ``form_class``. (default: ``ModelSearchForm``)
195 * page
196 The current page of search results.
197 * paginator
198 A paginator instance for the results.
199 * query
200 The query received by the form.
201 """
202 query = ''
203 results = EmptySearchQuerySet()
204
205 if request.GET.get('q'):
206 form = form_class(request.GET, searchqueryset=searchqueryset, load_all=load_all)
207
208 if form.is_valid():
209 query = form.cleaned_data['q']
210 results = form.search()
211 else:
212 form = form_class(searchqueryset=searchqueryset, load_all=load_all)
213
214 paginator = Paginator(results, results_per_page or RESULTS_PER_PAGE)
215
216 try:
217 page = paginator.page(int(request.GET.get('page', 1)))
218 except InvalidPage:
219 raise Http404("No such page of results!")
220
221 context = {
222 'form': form,
223 'page': page,
224 'paginator': paginator,
225 'query': query,
226 'suggestion': None,
227 }
228
229 if getattr(settings, 'HAYSTACK_INCLUDE_SPELLING', False):
230 context['suggestion'] = form.get_suggestion()
231
232 if extra_context:
233 context.update(extra_context)
234
235 return render_to_response(template, context, context_instance=context_class(request))