annotate custom_search/forms.py @ 887:9a15f7c27526

Actually save model object upon change. This commit was tested on the comments model. Additional logging added. Added check for Markdown image references. Added TODOs after observing behavior on comments.
author Brian Neal <bgneal@gmail.com>
date Tue, 03 Feb 2015 21:09:44 -0600
parents 20a3bf7a6370
children cf9918328c64
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@753 57 if not settings.SEARCH_QUEUE_ENABLED:
bgneal@753 58 raise forms.ValidationError("Our search function is offline for "
bgneal@753 59 "maintenance. Please try again later. "
bgneal@753 60 "We apologize for any inconvenience.")
bgneal@753 61
bgneal@753 62 if not (self.cleaned_data['q'] or self.cleaned_data['exact'] or
bgneal@753 63 self.cleaned_data['exclude']):
bgneal@753 64 raise forms.ValidationError('Please supply some search terms')
bgneal@753 65
bgneal@753 66 return self.cleaned_data
bgneal@753 67
bgneal@753 68 def search(self):
bgneal@753 69 if not self.is_valid():
bgneal@753 70 return self.no_query_found()
bgneal@753 71
bgneal@763 72 if self.user is None:
bgneal@763 73 username = 'UNKNOWN'
bgneal@763 74 elif self.user.is_authenticated():
bgneal@763 75 username = self.user.username
bgneal@763 76 else:
bgneal@763 77 username = 'ANONYMOUS'
bgneal@763 78
bgneal@763 79 logger.info('Search executed: /%s/%s/%s/ in %s by %s',
bgneal@753 80 self.cleaned_data['q'],
bgneal@753 81 self.cleaned_data['exact'],
bgneal@753 82 self.cleaned_data['exclude'],
bgneal@763 83 self.cleaned_data['models'],
bgneal@763 84 username)
bgneal@753 85
bgneal@753 86 sqs = self.searchqueryset
bgneal@753 87
bgneal@753 88 # Note that in Haystack 2.x content is untrusted and is automatically
bgneal@753 89 # auto-escaped for us.
bgneal@753 90 #
bgneal@753 91 # Filter on the q terms; these should be and'ed together:
bgneal@753 92 terms = self.cleaned_data['q'].split()
bgneal@753 93 for term in terms:
bgneal@753 94 sqs = sqs.filter(content=term)
bgneal@753 95
bgneal@753 96 # Exact words or phrases:
bgneal@753 97 if self.cleaned_data['exact']:
bgneal@753 98 sqs = sqs.filter(content__exact=self.cleaned_data['exact'])
bgneal@753 99
bgneal@753 100 # Exclude terms:
bgneal@753 101 terms = self.cleaned_data['exclude'].split()
bgneal@753 102 for term in terms:
bgneal@753 103 sqs = sqs.exclude(content=term)
bgneal@753 104
bgneal@753 105 if self.load_all:
bgneal@753 106 sqs = sqs.load_all()
bgneal@753 107
bgneal@753 108 # Apply model filtering
bgneal@753 109 sqs = sqs.models(*self.get_models())
bgneal@753 110
bgneal@753 111 return sqs