annotate custom_search/forms.py @ 989:2908859c2fe4

Smilies now use relative links. This is for upcoming switch to SSL. Currently we do not need absolute URLs for smilies. If this changes we can add it later.
author Brian Neal <bgneal@gmail.com>
date Thu, 29 Oct 2015 20:54:34 -0500
parents 6cc9221d04a7
children 2f36abf65a62
rev   line source
bgneal@469 1 """
bgneal@469 2 This module contains custom forms to tailor the Haystack search application to
bgneal@469 3 our needs.
bgneal@469 4
bgneal@469 5 """
bgneal@753 6 import logging
bgneal@753 7
bgneal@469 8 from django import forms
bgneal@753 9 from django.conf import settings
bgneal@469 10 from haystack.forms import ModelSearchForm
bgneal@469 11
bgneal@469 12
bgneal@469 13 MODEL_CHOICES = (
bgneal@469 14 ('forums.topic', 'Forum Topics'),
bgneal@469 15 ('forums.post', 'Forum Posts'),
bgneal@469 16 ('news.story', 'News Stories'),
bgneal@469 17 ('bio.userprofile', 'User Profiles'),
bgneal@469 18 ('weblinks.link', 'Links'),
bgneal@469 19 ('downloads.download', 'Downloads'),
bgneal@469 20 ('podcast.item', 'Podcasts'),
bgneal@469 21 ('ygroup.post', 'Yahoo Group Archives'),
bgneal@469 22 )
bgneal@469 23
bgneal@753 24 logger = logging.getLogger(__name__)
bgneal@753 25
bgneal@469 26
bgneal@469 27 class CustomModelSearchForm(ModelSearchForm):
bgneal@469 28 """
bgneal@469 29 This customized ModelSearchForm allows us to explictly label and order
bgneal@469 30 the model choices.
bgneal@469 31
bgneal@753 32 We also provide "all words", "exact phrase", and "exclude" text input boxes.
bgneal@753 33 Haystack 2.1.0's auto_query() function did not seem to work right so we just
bgneal@753 34 rolled our own.
bgneal@753 35
bgneal@763 36 This form can optionally receive the user making the search as a keyword
bgneal@763 37 argument ('user') to __init__. This will be used for logging search queries.
bgneal@763 38
bgneal@469 39 """
bgneal@753 40 q = forms.CharField(required=False, label='All these words',
bgneal@753 41 widget=forms.TextInput(attrs={'type': 'search', 'class': 'search',
bgneal@753 42 'size': 48}))
bgneal@753 43 exact = forms.CharField(required=False, label='This exact word or phrase',
bgneal@753 44 widget=forms.TextInput(attrs={'type': 'search', 'class': 'search',
bgneal@753 45 'size': 48}))
bgneal@753 46 exclude = forms.CharField(required=False, label='None of these words',
bgneal@753 47 widget=forms.TextInput(attrs={'type': 'search', 'class': 'search',
bgneal@753 48 'size': 48}))
bgneal@469 49
bgneal@469 50 def __init__(self, *args, **kwargs):
bgneal@763 51 self.user = kwargs.pop('user', None)
bgneal@469 52 super(CustomModelSearchForm, self).__init__(*args, **kwargs)
bgneal@469 53 self.fields['models'] = forms.MultipleChoiceField(choices=MODEL_CHOICES,
bgneal@753 54 label='Search in', widget=forms.CheckboxSelectMultiple)
bgneal@753 55
bgneal@753 56 def clean(self):
bgneal@956 57 super(CustomModelSearchForm, self).clean()
bgneal@753 58 if not settings.SEARCH_QUEUE_ENABLED:
bgneal@753 59 raise forms.ValidationError("Our search function is offline for "
bgneal@753 60 "maintenance. Please try again later. "
bgneal@753 61 "We apologize for any inconvenience.")
bgneal@753 62
bgneal@956 63 q = self.cleaned_data.get('q')
bgneal@956 64 exact = self.cleaned_data.get('exact')
bgneal@956 65 exclude = self.cleaned_data.get('exclude')
bgneal@956 66
bgneal@956 67 if not (q or exact or exclude):
bgneal@753 68 raise forms.ValidationError('Please supply some search terms')
bgneal@753 69
bgneal@753 70 return self.cleaned_data
bgneal@753 71
bgneal@943 72 def clean_exact(self):
bgneal@943 73 exact_field = self.cleaned_data['exact']
bgneal@943 74 if "'" in exact_field or '"' in exact_field:
bgneal@943 75 raise forms.ValidationError("Quotes are not needed in this field")
bgneal@943 76 return exact_field
bgneal@943 77
bgneal@753 78 def search(self):
bgneal@753 79 if not self.is_valid():
bgneal@753 80 return self.no_query_found()
bgneal@753 81
bgneal@763 82 if self.user is None:
bgneal@763 83 username = 'UNKNOWN'
bgneal@763 84 elif self.user.is_authenticated():
bgneal@763 85 username = self.user.username
bgneal@763 86 else:
bgneal@763 87 username = 'ANONYMOUS'
bgneal@763 88
bgneal@763 89 logger.info('Search executed: /%s/%s/%s/ in %s by %s',
bgneal@753 90 self.cleaned_data['q'],
bgneal@753 91 self.cleaned_data['exact'],
bgneal@753 92 self.cleaned_data['exclude'],
bgneal@763 93 self.cleaned_data['models'],
bgneal@763 94 username)
bgneal@753 95
bgneal@753 96 # Note that in Haystack 2.x content is untrusted and is automatically
bgneal@753 97 # auto-escaped for us.
bgneal@753 98 #
bgneal@943 99 # Gather regular search terms
bgneal@943 100 terms = ' '.join(self.cleaned_data['q'].split())
bgneal@753 101
bgneal@753 102 # Exact words or phrases:
bgneal@943 103 exact = self.cleaned_data['exact'].strip()
bgneal@943 104 if exact:
bgneal@943 105 exact = '"{}"'.format(exact)
bgneal@753 106
bgneal@753 107 # Exclude terms:
bgneal@943 108 exclude = ["-{}".format(term) for term in self.cleaned_data['exclude'].split()]
bgneal@943 109 exclude = ' '.join(exclude)
bgneal@943 110
bgneal@943 111 query = ' '.join([terms, exact, exclude]).strip()
bgneal@943 112 logger.debug("auto_query: %s", query)
bgneal@943 113
bgneal@943 114 sqs = self.searchqueryset.auto_query(query)
bgneal@753 115
bgneal@753 116 if self.load_all:
bgneal@753 117 sqs = sqs.load_all()
bgneal@753 118
bgneal@753 119 # Apply model filtering
bgneal@753 120 sqs = sqs.models(*self.get_models())
bgneal@753 121
bgneal@753 122 return sqs