comparison custom_search/receivers.py @ 924:78b459d4ab17

App refactor for custom_search for Django 1.7.7. upgrade. This commit prevents a lot of Django warnings by creating our Haystack signal processor as part of the custom_search apps' ready() method.
author Brian Neal <bgneal@gmail.com>
date Thu, 09 Apr 2015 19:43:07 -0500
parents custom_search/signals.py@ad53d929281a
children
comparison
equal deleted inserted replaced
923:bd043677d527 924:78b459d4ab17
1 """This module contains a custom Haystack signal processing class to update the
2 search index in realtime. We update our search index by enqueuing edits and
3 deletes into a queue for batch processing. Our class ensures we only enqueue
4 content that should be in the search index.
5
6 """
7 from django.db.models import signals
8 import queued_search.signals
9 import haystack
10
11 from bio.signals import profile_content_update
12 from forums.signals import topic_content_update, post_content_update
13
14 import ygroup.models
15 from weblinks.models import Link
16 from podcast.models import Item
17 from news.models import Story
18 from downloads.models import Download
19 from forums.models import Forum, Topic, Post
20 from bio.models import UserProfile
21
22
23 UID = 'custom_search.signals'
24
25
26 class QueuedSignalProcessor(queued_search.signals.QueuedSignalProcessor):
27 """
28 This customized version of queued_search's QueuedSignalProcessor
29 conditionally enqueues items to be indexed.
30
31 """
32 def __init__(self, *args, **kwargs):
33
34 # We assume that it is okay to attempt to delete a model from the search
35 # index even if the model object is not in the index. In other words,
36 # attempting to delete an object from the index will not cause any
37 # errors if it is not in the index. Thus if we see an object that has an
38 # 'is_public' attribute, and it is false, we can safely enqueue a delete
39 # in case the 'is_public' attribute just went from True to False. We
40 # have no way of knowing that, it could have been False all along, but we
41 # just try the delete in case to be safe.
42
43 # To make the code easier to read, use a table to drive our signal
44 # connecting and disconnecting:
45 self.signal_chain = [
46 # Yahoo Group posts are always updated:
47 (signals.post_save, ygroup.models.Post, self.enqueue_save),
48 (signals.post_delete, ygroup.models.Post, self.enqueue_delete),
49
50 # Weblink Links are updated if they are public:
51 (signals.post_save, Link, self.enqueue_public_save),
52 (signals.post_delete, Link, self.enqueue_delete),
53
54 # Podcast Items are always updated:
55 (signals.post_save, Item, self.enqueue_save),
56 (signals.post_delete, Item, self.enqueue_delete),
57
58 # News Stories are always updated:
59 (signals.post_save, Story, self.enqueue_save),
60 (signals.post_delete, Story, self.enqueue_delete),
61
62 # Downloads are updated if they are public:
63 (signals.post_save, Download, self.enqueue_public_save),
64 (signals.post_delete, Download, self.enqueue_delete),
65
66 # Forum Topics are updated if they belong to a public forum:
67 (topic_content_update, None, self.enqueue_topic_save),
68 (signals.post_delete, Topic, self.enqueue_delete),
69
70 # Forum Posts are updated if they belong to a public forum:
71 (post_content_update, None, self.enqueue_post_save),
72 (signals.post_delete, Post, self.enqueue_delete),
73
74 # UserProfiles are updated when we receive a special signal:
75 (profile_content_update, None, self.enqueue_profile),
76 (signals.post_delete, UserProfile, self.enqueue_delete),
77 ]
78
79 super(QueuedSignalProcessor, self).__init__(*args, **kwargs)
80
81 def setup(self):
82 """We override setup() so we can attach signal handlers to only the
83 models we search on. In some cases we have custom signals to tell us
84 when to update the search index.
85
86 """
87 for signal, sender, receiver in self.signal_chain:
88 signal.connect(receiver, sender=sender, dispatch_uid=UID)
89
90 def teardown(self):
91 """Disconnect all signals we previously connected."""
92 for signal, sender, receiver in self.signal_chain:
93 signal.disconnect(receiver, sender=sender, dispatch_uid=UID)
94
95 def enqueue_public_save(self, sender, instance, **kwargs):
96 """Index only if the instance is_public.
97
98 If not, enqueue a delete just in case the is_public flag got flipped
99 from True to False.
100
101 """
102 if instance.is_public:
103 self.enqueue_save(sender, instance, **kwargs)
104 else:
105 self.enqueue_delete(sender, instance, **kwargs)
106
107 def enqueue_topic_save(self, sender, **kwargs):
108 """Enqueue only if the topic instance belongs to a public forum."""
109 if sender.forum.id in Forum.objects.public_forum_ids():
110 self.enqueue_save(Topic, sender, **kwargs)
111
112 def enqueue_post_save(self, sender, **kwargs):
113 """Enqueue only if the post instance belongs to a public forum."""
114 if sender.topic.forum.id in Forum.objects.public_forum_ids():
115 self.enqueue_save(Post, sender, **kwargs)
116
117 def enqueue_profile(self, sender, **kwargs):
118 """Forward the user profile instance on unconditionally."""
119 self.enqueue_save(UserProfile, sender, **kwargs)
120
121
122 # Starting with Django 1.7, we'd see Django generate warnings if we defined
123 # a HAYSTACK_SIGNAL_PROCESSOR in our settings that referenced the class above.
124 # This is because Haystack creates an instance of our signal processor class
125 # (defined above) at import time, and thus imports this module very early in the
126 # application startup sequence. Warnings are then generated when this module
127 # imports our models, some of whose applications have not been imported yet.
128 # This problem will presumably go away when Haystack can fully support Django
129 # 1.7.x and implements an AppConfig with a ready() method. Until then, we don't
130 # use Haystack's signal processor object; we'll just create one here. This
131 # module will be imported when our custom_search app's ready() method runs.
132
133 signal_processor = QueuedSignalProcessor(haystack.connections,
134 haystack.connection_router)