|
| 1 | +============= |
| 2 | +Views & Forms |
| 3 | +============= |
| 4 | + |
| 5 | +Haystack comes with some default, simple views & forms to help you get started |
| 6 | +and to cover the common cases. Included is a way to provide: |
| 7 | + |
| 8 | + * Basic, query-only search. |
| 9 | + * Search by models. |
| 10 | + * Search with basic highlighted results. |
| 11 | + * Faceted search. |
| 12 | + * Search by models with basic highlighted results. |
| 13 | + |
| 14 | +Most processing is done by the forms provided by Haystack via the ``search`` |
| 15 | +method. As a result, all but the faceted types (see :doc:`faceting`) use the |
| 16 | +standard ``SearchView``. |
| 17 | + |
| 18 | +There is very little coupling between the forms & the views (other than relying |
| 19 | +on the existence of a ``search`` method on the form), so you may interchangeably |
| 20 | +use forms and/or views anywhere within your own code. |
| 21 | + |
| 22 | +Forms |
| 23 | +===== |
| 24 | + |
| 25 | +``SearchForm`` |
| 26 | +-------------- |
| 27 | + |
| 28 | +The most basic of the form types, this form consists of a single field, the |
| 29 | +``q`` field (for query). Upon searching, the form will take the cleaned contents |
| 30 | +of the ``q`` field and perform an ``auto_query`` on either the custom |
| 31 | +``SearchQuerySet`` you provide or off a default ``SearchQuerySet``. |
| 32 | + |
| 33 | +To customize the ``SearchQuerySet`` the form will use, pass it a |
| 34 | +``searchqueryset`` parameter to the constructor with the ``SearchQuerySet`` |
| 35 | +you'd like to use. If using this form in conjunction with a ``SearchView``, |
| 36 | +the form will receive whatever ``SearchQuerySet`` you provide to the view with |
| 37 | +no additional work needed. |
| 38 | + |
| 39 | +All other forms in Haystack inherit (either directly or indirectly) from this |
| 40 | +form. |
| 41 | + |
| 42 | +``HighlightedSearchForm`` |
| 43 | +------------------------- |
| 44 | + |
| 45 | +Identical to the ``SearchForm`` except that it tags the ``highlight`` method on |
| 46 | +to the end of the ``SearchQuerySet`` to enable highlighted results. |
| 47 | + |
| 48 | +``ModelSearchForm`` |
| 49 | +------------------- |
| 50 | + |
| 51 | +This form adds new fields to form. It iterates through all registered models for |
| 52 | +the current ``SearchSite`` and provides a checkbox for each one. If no models |
| 53 | +are selected, all types will show up in the results. |
| 54 | + |
| 55 | +``HighlightedModelSearchForm`` |
| 56 | +------------------------------ |
| 57 | + |
| 58 | +Identical to the ``ModelSearchForm`` except that it tags the ``highlight`` |
| 59 | +method on to the end of the ``SearchQuerySet`` to enable highlighted results on |
| 60 | +the selected models. |
| 61 | + |
| 62 | +``FacetedSearchForm`` |
| 63 | +--------------------- |
| 64 | + |
| 65 | +Identical to the ``SearchForm`` except that it adds a hidden ``selected_facets`` |
| 66 | +field onto the form, allowing the form to narrow the results based on the facets |
| 67 | +chosen by the user. |
| 68 | + |
| 69 | +Creating Your Own Form |
| 70 | +---------------------- |
| 71 | + |
| 72 | +The simplest way to go about creating your own form is to inherit from |
| 73 | +``SearchForm`` (or the desired parent) and extend the ``search`` method. By |
| 74 | +doing this, you save yourself most of the work of handling data correctly and |
| 75 | +stay API compatible with the ``SearchView``. |
| 76 | + |
| 77 | +For example, let's say you're providing search with a user-selectable date range |
| 78 | +associated with it. You might create a form that looked as follows:: |
| 79 | + |
| 80 | + from django import forms |
| 81 | + from haystack.forms import SearchForm |
| 82 | + |
| 83 | + |
| 84 | + class DateRangeSearchForm(SearchForm): |
| 85 | + start_date = forms.DateField(required=False) |
| 86 | + end_date = forms.DateField(required=False) |
| 87 | + |
| 88 | + def search(self): |
| 89 | + # First, store the SearchQuerySet received from other processing. |
| 90 | + sqs = super(DateRangeSearchForm, self).search() |
| 91 | + |
| 92 | + # Check to see if a start_date was chosen. |
| 93 | + if self.cleaned_data['start_date']: |
| 94 | + sqs = sqs.filter(pub_date__gte=self.cleaned_data['start_date']) |
| 95 | + |
| 96 | + # Check to see if an end_date was chosen. |
| 97 | + if self.cleaned_data['end_date']: |
| 98 | + sqs = sqs.filter(pub_date__lte=self.cleaned_data['end_date']) |
| 99 | + |
| 100 | + return sqs |
| 101 | + |
| 102 | +This form adds two new fields for (optionally) choosing the start and end dates. |
| 103 | +Within the ``search`` method, we grab the results from the parent form's |
| 104 | +processing. Then, if a user has selected a start and/or end date, we apply that |
| 105 | +filtering. Finally, we simply return the ``SearchQuerySet``. |
| 106 | + |
| 107 | +Views |
| 108 | +===== |
| 109 | + |
| 110 | +Haystack comes bundled with two views, the ``SearchView`` and the |
| 111 | +``FacetedSearchView``. It uses class-based views for easy extension should you |
| 112 | +need to alter the way a view works. Except in the case of faceting (again, see |
| 113 | +:doc:`faceting`), the ``SearchView`` works interchangeably with all other forms |
| 114 | +provided by Haystack. |
| 115 | + |
| 116 | +``SearchView(template=None, load_all=True, form_class=ModelSearchForm, searchqueryset=None, context_class=RequestContext)`` |
| 117 | +--------------------------------------------------------------------------------------------------------------------------- |
| 118 | + |
| 119 | +The ``SearchView`` is designed to be easy/flexible enough to override common |
| 120 | +changes as well as being internally abstracted so that only altering a specific |
| 121 | +portion of the code should be easy to do. |
| 122 | + |
| 123 | +Without touching any of the internals of the ``SearchView``, you can modify |
| 124 | +which template is used, which form class should be instantiated to search with, |
| 125 | +what ``SearchQuerySet`` to use in the event you wish to pre-filter the results. |
| 126 | +what ``Context``-style object to use in the response and the ``load_all`` |
| 127 | +performance optimization to reduce hits on the database. These options can (and |
| 128 | +generally should) be overridden at the URLconf level. For example, to have a |
| 129 | +custom search limited to the 'John' author, displaying all models to search by |
| 130 | +and specifying a custom template (``my/special/path/john_search.html``), your |
| 131 | +URLconf should look something like:: |
| 132 | + |
| 133 | + from django.conf.urls.defaults import * |
| 134 | + from haystack.forms import ModelSearchForm |
| 135 | + from haystack.query import SearchQuerySet |
| 136 | + from haystack.views import SearchView |
| 137 | + |
| 138 | + sqs = SearchQuerySet().filter(author='john') |
| 139 | + |
| 140 | + urlpatterns = patterns('haystack.views', |
| 141 | + url(r'^$', SearchView( |
| 142 | + template='my/special/path/john_search.html', |
| 143 | + searchqueryset=sqs, |
| 144 | + form_class=ModelSearchForm |
| 145 | + ), name='haystack_search'), |
| 146 | + ) |
| 147 | + |
| 148 | +Beyond this customizations, you can create your own ``SearchView`` and |
| 149 | +extend/override the following methods to change the functionality. |
| 150 | + |
| 151 | +``__call__(self, request)`` |
| 152 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 153 | + |
| 154 | +Generates the actual response to the search. |
| 155 | + |
| 156 | +Relies on internal, overridable methods to construct the response. You generally |
| 157 | +should avoid altering this method unless you need to change the flow of the |
| 158 | +methods or to add a new method into the processing. |
| 159 | + |
| 160 | +``build_form(self)`` |
| 161 | +~~~~~~~~~~~~~~~~~~~~ |
| 162 | + |
| 163 | +Instantiates the form the class should use to process the search query. |
| 164 | + |
| 165 | +You should override this if you write a custom form that needs special |
| 166 | +parameters for instantiation. |
| 167 | + |
| 168 | +``get_query(self)`` |
| 169 | +~~~~~~~~~~~~~~~~~~~ |
| 170 | + |
| 171 | +Returns the query provided by the user. |
| 172 | + |
| 173 | +Returns an empty string if the query is invalid. This pulls the cleaned query |
| 174 | +from the form, via the ``q`` field, for use elsewhere within the ``SearchView``. |
| 175 | +This is used to populate the ``query`` context variable. |
| 176 | + |
| 177 | +``get_results(self)`` |
| 178 | +~~~~~~~~~~~~~~~~~~~~~ |
| 179 | + |
| 180 | +Fetches the results via the form. |
| 181 | + |
| 182 | +Returns an empty list if there's no query to search with. This method relies on |
| 183 | +the form to do the heavy lifting as much as possible. |
| 184 | + |
| 185 | +``build_page(self)`` |
| 186 | +~~~~~~~~~~~~~~~~~~~~ |
| 187 | + |
| 188 | +Paginates the results appropriately. |
| 189 | + |
| 190 | +In case someone does not want to use Django's built-in pagination, it |
| 191 | +should be a simple matter to override this method to do what they would |
| 192 | +like. |
| 193 | + |
| 194 | +``extra_context(self)`` |
| 195 | +~~~~~~~~~~~~~~~~~~~~~~~ |
| 196 | + |
| 197 | +Allows the addition of more context variables as needed. Must return a |
| 198 | +dictionary whose contents will add to or overwrite the other variables in the |
| 199 | +context. |
| 200 | + |
| 201 | +``create_response(self)`` |
| 202 | +~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 203 | + |
| 204 | +Generates the actual HttpResponse to send back to the user. It builds the page, |
| 205 | +creates the context and renders the response for all the aforementioned |
| 206 | +processing. |
| 207 | + |
| 208 | +Creating Your Own View |
| 209 | +---------------------- |
| 210 | + |
| 211 | +As with the forms, inheritance is likely your best bet. In this case, the |
| 212 | +``FacetedSearchView`` is a perfect example of how to extend the existing |
| 213 | +``SearchView``. The complete code for the ``FacetedSearchView`` looks like:: |
| 214 | + |
| 215 | + class FacetedSearchView(SearchView): |
| 216 | + def __name__(self): |
| 217 | + return "FacetedSearchView" |
| 218 | + |
| 219 | + def extra_context(self): |
| 220 | + extra = super(FacetedSearchView, self).extra_context() |
| 221 | + |
| 222 | + if self.results == []: |
| 223 | + extra['facets'] = self.form.search().facet_counts() |
| 224 | + else: |
| 225 | + extra['facets'] = self.results.facet_counts() |
| 226 | + |
| 227 | + return extra |
| 228 | + |
| 229 | +It updates the name of the class (generally for documentation purposes) and |
| 230 | +adds the facets from the ``SearchQuerySet`` to the context as the ``facets`` |
| 231 | +variable. As with the custom form example above, it relies on the parent class |
| 232 | +to handle most of the processing and extends that only where needed. |
0 commit comments