How can I save a model, such that signals arent sent. (post_save and pre_save)
It's a bit of a hack, but you can do something like this:
use a unique identifier with a filter and then use the update method of the queryset (which does not trigger the signals)
user_id = 142187
User.objects.filter(id=user_id).update(name='tom')
ModelName.objects.bulk_create([your object/objects])
also you can read more here django docs
This ticket has been marked as "wontfix" because:
In short, it sounds like, given the defined purpose of signals, it is
the attached signal handler that needs to become more intelligent
(like in davedash's suggestion), rather than the code that emits the
signal. Disabling signals is just a quick fix that will work when you
know exactly what handlers are attached to a signal, and it hides the
underlying problem by putting the fix in the wrong place.
There is currently a ticket pending a Django design decision for this feature.
Included in the ticket is a diff for a patch with the proposed implementation.
If you have mutual relations on models and their signals
still you can decouple signal's logic to have more signals of same type, and handle your logic in more sophisticated way:
You can check in signals, the state of object:
kwargs['created']
You can check the state of any pasted extra value:
So in one signal, you will read at first:
if `kwargs['instance'].skip_signals`:
return
and in another place, before save() you will just set that skip_signals on the specific object, in specific situation.
(there is no need to define it as model field)
You can also do not emit signals:
by overriding method(s) on models,
or by adding own save_without_signals(),
or just as mentioned already, doing filter(pk=<>).update(...)
As far as I'm aware there still isn't a 'nice' way to do this, but if you're open to exploring hacky solutions, then I'll add one to the mix.
If you look at the django model source code, specifically save_base() here and here, you'll see that the pre_save() and post_save() signals are both wrapped in a conditional:
if not meta.auto_created:
// Emit signal
We can directly manipulate the meta options of a model or instance through the _meta API which means we're able to 'disable' the signals from firing by setting auto_created = True on the instance we want to save.
For example:
#receiver(post_save, sender=(MyModel))
def save_my_model(sender, instance=None, created=False, **kwargs):
if created:
# Modify the instance
instance.task_id = task.task_hash
# HACK: Prevent `post_save` signal from being called during save
instance._meta.auto_created = True
instance.save()
instance._meta.auto_created = False
elif instance.has_changed("schedule"):
# Modify the instance
instance.task_id = 'abc123'
# HACK: Prevent `post_save` signal from being called during save
instance._meta.auto_created = True
instance.save()
instance._meta.auto_created = False
The major caveat here is that this is undocumented behaviour and it could well change it future releases of django.
Related
I want to make changes in a model instance A, when a second model instance B
is saved,updated or deleted.
All models are in the same Django app.
What would be the optimal way to do it?
Should I use signals?
Override default methods[save, update,delete]?
Something else?
Django documentation warns:
Where possible you should opt for directly calling the handling code, rather than dispatching via a signal.
Can somebody elaborate on that statement?
The performance impact of your signal handlers depends of course on their functionality. But you should be aware of the fact that they are executed synchronously when the signal is fired, so if there's a lot of action going on in the handlers (for example a lot of database calls) the execution will be delayed.
I have a model where i would like when an object gets deleted, instead of being deleted a status is updated. This was achieved with following code:
def delete(self, using=None, keep_parents=False):
self.status = Booking.DELETED
self.save()
The manager was updated so that in the rest of the application i never get presented deleted bookings.
class BookingManager(models.Manager):
def get_queryset(self):
return super().get_queryset().exclude(status=Booking.DELETED)
class BookingDeletedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status=Booking.DELETED)
class Booking(models.Model):
PAYED = 0
PENDING = 1
OPEN = 2
CANCELLED = 3
DELETED = 4
objects = BookingManager()
deleted_objects = BookingDeletedManager()
...
Now i have read up on django signals and was wondering wheter it would be better to use the pre delete signal here. The code could be changed so that in the pre delete receiver a duplicate is created with status 'deleted' and the original booking is just deleted.
In the documentation it states that these signals should be used to allow decoupled applications get notified when actions occur elsewhere in the framework. It seems the signal is a good a solution but this nuance in the documentation makes me think it's maybe not what i want and overriding might just be the way to go.
This is not really the case here since i just want this functionality all the time. So my question being is there a solid reason why i should not override the delete method and use the pre delete signal or vice versa?
In the documentation it states that these signals should be used to allow decoupled applications get notified when actions occur elsewhere in the framework.
That's indeed the point: allowing one application to get notified of events occuring in another application that knows nothing about the first one - hence avoiding the need to couple the second application to the first one.
In your case using models signals instead of just overridding the model's method would be an anti-pattern: it will only add overhead and make your code less readable for absolutely no good reason, when the very obvious solution is to just do what you've did.
This question is a continuation of one I asked yesterday: I'm still not sure if a post_save handler or a 2nd Celery task is the best way to update many objects based on the results of the first Celery task, but I plan to test performance down the line. Here's a recap of what's happening:
Celery task, every 30s:
Update page_count field of Book object based on conditions
|
post_save(Book) |
V
Update some field on all Reader objects w/ foreign key to updated Book
(update will have different results per-Reader, thousands of Readers could be FKed to Book)
The first task could save ~10 objects, requiring the update to all related Reader objects for each.
Whichever proves to be better between post_save and another task, they must accomplish the same thing: update potentially tens to hundreds of thousands of objects in a table, with each object update being unique. It could be that my choice between post_save and Celery task is determined by which method will actually allow me to accomplish this goal.
Since I can't just use a few queryset update() commands, I need to somehow call a method or function that calculates the value of a field based on the result of the first Celery task as well as some of the values in the object. Here's an example:
class Reader(models.Model):
book = models.ForeignKey(Book)
pages_read = models.IntegerField(default=0)
book_finished = models.BooleanField(default=False)
def determine_book_finished(self):
if self.pages_read == book.page_count:
self.book_finished = True
else:
self.book_finished = False
This is a contrived example, but if the page_count was updated in the first task, I want all Readers foreign keyed to the Book to have their book_finished recalculated- and looping over a queryset seems like a really inefficient way to go about it.
My thought was to somehow call a model method such as determine_book_finished() on an entire queryset at once, but I can't find any documentation on how to do something like that- custom querysets don't appear to be intended for actually operating on objects in the queryset beyond the built-in update() capability.
This post using Celery is the most promising thing I've found, and since Django signals are sync, using another Celery task would also have the benefit of not holding anything else up. So even though I'd still need to loop over a queryset, it'd be async and any querysets that needed to be updated could be handled by separate tasks, hopefully in parallel.
On the other had, this question seems to have a solution too- register the method with the post_save signal, which presumably would run the method on all objects after receiving the signal. Would this be workable with thousands of objects needing update, as well as potentially other Books being updated by the same task and their thousands of associated Readers then needing update too?
Is there a best practice for doing what I'm trying to do here?
EDIT: I realized I could go about this another way- making the book_finished field a property determined at runtime rather than a static field.
#property
def book_finished:
if self.pages_read == self.book.page_count:
if self.book.page_count == self.book.planned_pages:
return True
else:
return False
This is close enough to my actual code- in that, the first if branch contains a couple elif branches, with each having their own if-else for a total maximum depth of 3 ifs.
Until I can spin up a lot of test data and simulate many simultaneous users, I may stick with this option as it definitely works (for now). I don't really like having the property calculated every retrieval, but from some quick research, it doesn't seem like an overly slow method.
I have a class from which all my entity definitions inherit:
class Model(db.Model):
"""Superclass for all others; contains generic properties and methods."""
created = db.DateTimeProperty(auto_now_add=True)
modified = db.DateTimeProperty(auto_now=True)
For various reasons I want to be able to occasionally modify an entity without changing its modified property. I found this example:
Model.__dict__["modified"].__dict__["auto_now"] = False
db.put(my_entity)
Model.__dict__["modified"].__dict__["auto_now"] = True
When I test this locally, it works great. My question is this: could this have wider ramifications for any other code that happens to be saving entities during the small period of time Model is altered? I could see that leading to incredibly confusing bugs. But maybe this little change only affects the current process/thread or whatever?
Any other request coming in to the same instance and being handled whilst the put is in progress will also get auto_now=False, whilst unlikely it is possible
Something else other thing to consider
You don't have try block around this code, if you get a timeout or error during the put() your code will leave the model in the modified state with auto_now=False .
Personally in think its a bad idea and will definatley be a source of errors.
There are a number of ways of achieving this without manipulating models,
consider setting the default behaviour to auto_now=False, and then have two methods you use for updating. The primary method sets the modified time to datetime.now() just before you do the put(), e.g save() and save_without_modified()
A better method would to override put() in your class, then set modified and then call super put() have put() accept a new argument like modified=False so you don't set the modified date before you call super.
Lastly you could use _pre_put hook to run code before the put() call, but you need to annotate the instance in some way so the _pre_put method can determine if modified needs to be set or not.
I think each of these strategies is a lot more safe than hacking the model
Using wxpython in MVC, I looked for a way to let the models tell the controllers about changes. I found (py)pubsub, which implements a global notification mechanism: Messages are sent to one place (the pubsub Publisher), which sends them to all subscribers. Each subscriber checks whether the message is interesting, and does what is needed.
From Smalltalk times, I know a more "local" approach: Each model object keeps a list of interested controllers, and only sends change notifications to these. No global publisher is involved. This can be implemented as part of the Model class, and works in much the same way, except it's local to the model and the controller.
Now is there a reason to use the global approach (which seems much less performant to me, and might be prone to all the issues related to global approaches)? Is there another package implementing a local observer?
Thanks!
I'm not really seeing the subtle difference here. As far as I know, pubsub is the way to go. It's included in wxPython in wx.lin.pubsub or you can download it from http://pubsub.sourceforge.net/. You can put the listeners just in the models and the publisher(s) just in the controller or however you need to. Here are a couple links to get you started:
http://www.blog.pythonlibrary.org/2010/06/27/wxpython-and-pubsub-a-simple-tutorial/
http://wiki.wxpython.org/WxLibPubSub
I've been playing around for a while to do MVC with wxpython and i know what you mean about pubsub being global.
The latest idea i've come up with is each view and model have there own observer.
The observers have weak references to their handlers and it all works in a separate thread so as to not block the GUI. To call back to the GUI thread I'm using wxAnyThread Gui method decorator.
There are 3 types of signal that get sent, for the model you can set which attributes are observed they automatically send out a signal when they are changed. then on both the model and the view you can send a message signal or a keyword signal. Each of the three signal types have to be unique per view or model as they are used to make a tuple that identify them.
model attributes
controller handlers are decorated with
#onAttr('attributeName')
def onModelAttributeName(self, attributeName)
When you bind to a method that handlers attributes it straight away calls the handler with its current value and then continues to observe changes.
Sending messages
Use the method
view/model.notify('Your message'):
The controller callback is decorated with
#onNotify('Your message')
def onYourMessage(self):
Sending keywords
Use the method
view/model.notifyKw(valid=True, value='this)
The controller callback is decorated with
#onNotifyKw('valid', 'value')
def onValidValueKw(self, valid, value)
The GUI is left knowing nothing about the models the only thing you add to the GUI is the view signaler, the controller attaches it self to this, so if you don't add a controller the view will just happily fire off messages to no one.
I've uploaded what i have so far on github
https://github.com/Yoriz/Y_Signal
https://github.com/Yoriz/Y_Mvc
Both have unit test which should give a bit of an example of what it does, but i will create some wxpython examples.
I'm using python version 2.7 and the Ysignals module requires
https://pypi.python.org/pypi/futures/2.1.3 for the threading.
Please take a look ill be interested in what someone else thinks of this way of approaching mvc or to point out something i seriously overlooked.