diff --git a/django/db/models/base.py b/django/db/models/base.py
index 71fd1f7..730e52f 100644
a
|
b
|
from django.db.models.query_utils import DeferredAttribute
|
18 | 18 | from django.db.models.deletion import Collector |
19 | 19 | from django.db.models.options import Options |
20 | 20 | from django.db.models import signals |
| 21 | from django.db.models.signals import pre_init, post_init |
21 | 22 | from django.db.models.loading import register_models, get_model |
22 | 23 | from django.utils.translation import ugettext_lazy as _ |
23 | 24 | from django.utils.functional import curry |
… |
… |
class Model(object):
|
277 | 278 | _deferred = False |
278 | 279 | |
279 | 280 | def __init__(self, *args, **kwargs): |
280 | | signals.pre_init.send(sender=self.__class__, args=args, kwargs=kwargs) |
| 281 | pre_init.send(sender=self.__class__, args=args, kwargs=kwargs) |
281 | 282 | |
282 | 283 | # Set up the storage for instance state |
283 | 284 | self._state = ModelState() |
… |
… |
class Model(object):
|
367 | 368 | if kwargs: |
368 | 369 | raise TypeError("'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]) |
369 | 370 | super(Model, self).__init__() |
370 | | signals.post_init.send(sender=self.__class__, instance=self) |
| 371 | post_init.send(sender=self.__class__, instance=self) |
371 | 372 | |
372 | 373 | def __repr__(self): |
373 | 374 | try: |
diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py
index ed9da57..989d86d 100644
a
|
b
|
class Signal(object):
|
31 | 31 | if providing_args is None: |
32 | 32 | providing_args = [] |
33 | 33 | self.providing_args = set(providing_args) |
| 34 | self.sender_no_receivers_cache = set() |
34 | 35 | self.lock = threading.Lock() |
35 | 36 | |
36 | 37 | def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): |
… |
… |
class Signal(object):
|
107 | 108 | else: |
108 | 109 | self.receivers.append((lookup_key, receiver)) |
109 | 110 | finally: |
| 111 | self.sender_no_receivers_cache = set() |
110 | 112 | self.lock.release() |
111 | 113 | |
112 | 114 | def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None): |
… |
… |
class Signal(object):
|
144 | 146 | del self.receivers[index] |
145 | 147 | break |
146 | 148 | finally: |
| 149 | self.sender_no_receivers_cache = set() |
147 | 150 | self.lock.release() |
148 | 151 | |
149 | 152 | def send(self, sender, **named): |
… |
… |
class Signal(object):
|
165 | 168 | Returns a list of tuple pairs [(receiver, response), ... ]. |
166 | 169 | """ |
167 | 170 | responses = [] |
168 | | if not self.receivers: |
| 171 | if sender in self.sender_no_receivers_cache: |
169 | 172 | return responses |
170 | 173 | |
171 | | for receiver in self._live_receivers(_make_id(sender)): |
| 174 | for receiver in self._live_receivers(sender): |
172 | 175 | response = receiver(signal=self, sender=sender, **named) |
173 | 176 | responses.append((receiver, response)) |
174 | 177 | return responses |
… |
… |
class Signal(object):
|
197 | 200 | receiver. |
198 | 201 | """ |
199 | 202 | responses = [] |
200 | | if not self.receivers: |
| 203 | if sender in self.sender_no_receivers_cache: |
201 | 204 | return responses |
202 | 205 | |
203 | 206 | # Call each receiver with whatever arguments it can accept. |
204 | 207 | # Return a list of tuple pairs [(receiver, response), ... ]. |
205 | | for receiver in self._live_receivers(_make_id(sender)): |
| 208 | for receiver in self._live_receivers(sender): |
206 | 209 | try: |
207 | 210 | response = receiver(signal=self, sender=sender, **named) |
208 | 211 | except Exception, err: |
… |
… |
class Signal(object):
|
211 | 214 | responses.append((receiver, response)) |
212 | 215 | return responses |
213 | 216 | |
214 | | def _live_receivers(self, senderkey): |
| 217 | def _live_receivers(self, sender): |
215 | 218 | """ |
216 | 219 | Filter sequence of receivers to get resolved, live receivers. |
217 | 220 | |
… |
… |
class Signal(object):
|
219 | 222 | live receivers. |
220 | 223 | """ |
221 | 224 | none_senderkey = _make_id(None) |
| 225 | senderkey = _make_id(sender) |
222 | 226 | receivers = [] |
223 | | |
| 227 | # Here is a trick which I believe is safe, but I haven't actually |
| 228 | # proved it. We store a reference to current sender_no_receivers_cache |
| 229 | # If there are modifications to self.receivers, then the |
| 230 | # modifier will create a new object for self.sender_no_receivers. |
| 231 | # Thus, if we decide to store something in local_sender_no_receivers |
| 232 | # cache using stale self.receivers info, there is no harm, because |
| 233 | # the reference itself is stale! Of course, this needs a more |
| 234 | # convincing comment, or maybe a proof... |
| 235 | local_sender_no_receivers = self.sender_no_receivers_cache |
224 | 236 | for (receiverkey, r_senderkey), receiver in self.receivers: |
225 | 237 | if r_senderkey == none_senderkey or r_senderkey == senderkey: |
226 | 238 | if isinstance(receiver, WEAKREF_TYPES): |
… |
… |
class Signal(object):
|
230 | 242 | receivers.append(receiver) |
231 | 243 | else: |
232 | 244 | receivers.append(receiver) |
| 245 | if not receivers: |
| 246 | local_sender_no_receivers.add(sender) |
233 | 247 | return receivers |
234 | 248 | |
235 | 249 | def _remove_receiver(self, receiver): |
236 | 250 | """ |
237 | 251 | Remove dead receivers from connections. |
238 | 252 | """ |
239 | | |
240 | 253 | self.lock.acquire() |
241 | 254 | try: |
242 | 255 | to_remove = [] |
… |
… |
class Signal(object):
|
251 | 264 | if r_key == key: |
252 | 265 | del self.receivers[last_idx-idx] |
253 | 266 | finally: |
| 267 | self.sender_no_receivers_cache = set() |
254 | 268 | self.lock.release() |
255 | 269 | |
256 | 270 | |