/bangkokhotel/lib/python2.5/site-packages/haystack/views.py

https://bitbucket.org/luisrodriguez/bangkokhotel · Python · 235 lines · 195 code · 19 blank · 21 comment · 7 complexity · 50a4907458326ee88c2bea3c65a67ab7 MD5 · raw file

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