django signals with bulk delete/update - django

I can't find any documentation on why does this happen, but according to the docs bulk operations should not trigger models signals.
Now the issue is this, if i do somequeryset.delete()a signal is triggered for each deleted object even if it is a bulk operation!
On the other hand, somequeryset.update(someField=5) will NOT trigger any signal!
So this is pretty much an unexpected result, I would hope for both to behave the same.
Django 1.7.7
Any ideas? I want deletes to have a signal but triggering it on bulk deletes is not acceptable

As explained here it really doesn't call delete() method on each item but it DOES calls signals. I don't know if that is possible but I also agree that there should be at least some option en queryset.delete() to skip signals execution.

Related

Django Transactions: How to run extra code during rollback?

Imagine you have a User model in your web app, and that you need to keep this user in sync with an external service via an API. Thus, when you create a user locally, you need to create it remotely as well.
You have all your operations under transaction.atomic() and you try to keep all your 3rd-party API calls after the atomic block, which is reasonable.
But, a system being a system, it grows in complexity until the point you have some really hard to remove 3rd-party calls within an update call.
That said, is there a way to extend Django's transaction mechanism, kind of adding some callback functions, like rollback.add_callback(clean_3rdparty_user(user_id=134))?
That way I can guarantee that all necessary rollback actions are taken and my system is in sync?
The author of Django's transaction hook code has this to say about why there is on_commit() but not on_rollback():
A rollback hook is even harder to implement robustly than a commit hook, since a variety of things can cause an implicit rollback. For instance, your database connection was dropped because your process was killed without a chance to shutdown gracefully: your rollback hook will never run.
Since rollbacks are typically triggered by an exception, a simple approach is to just catch any exceptions and run your undo code there.
try:
with transaction.atomic():
# Do database stuff
# Do external stuff
except:
# We know the database stuff has rolled back, so...
# Undo external stuff
raise
This is not particularly elegant. I agree with the following from the same source:
The solution is simple: instead of doing something during the atomic block (transaction) and then undoing it if the transaction fails, use on_commit to delay doing it in the first place until after the transaction succeeds. It’s a lot easier to undo something you never did in the first place!
But it sounds like you already agree with that as well.

Does SendAsyncCancel cancel SendMailAsync?

In the SmtpClient class, does SendAsyncCancel cancel the SendMailAsync method?
I see a few code examples on the www which imply it does.
However MSDN says,
Use the SendAsyncCancel method to cancel a pending SendAsync operation. If there is mail waiting to be sent, this method releases resources used to store the mail. If there is no mail waiting to be sent, this method does nothing.
... which implies that it cancels SendAsync but not SendMailAsync.
Is there a way to cancel SendMailAsync? If not, why not?
If you want to cancel an asynchronous send (therefore use the old SendAsync instead of the newer SendMailAsync), what are any other disadvantages of using SendAsync instead of SendMailAsync?
I tried to invoke SendMailAsync from a post-back handler of an ASP web page, and it threw an exception:
System.InvalidOperationException: Asynchronous operations are not allowed in this context. Page starting an asynchronous operation has to have the Async attribute set to true and an asynchronous operation can only be started on a page prior to PreRenderComplete event.
at System.Web.LegacyAspNetSynchronizationContext.OperationStarted()
at System.ComponentModel.AsyncOperation.CreateOperation(Object userSuppliedState, SynchronizationContext syncContext)
at System.Net.Mail.SmtpClient.SendAsync(MailMessage message, Object userToken)
--- End of inner exception stack trace ---
at System.Net.Mail.SmtpClient.SendAsync(MailMessage message, Object userToken)
at System.Net.Mail.SmtpClient.SendMailAsync(MailMessage message)
at MyWebSite.AdminTest.TestEmail.<sendAsynchronous>d__9.MoveNext()
From this I deduce two things:
SendMailAsync is indeed implemented using SendAsync (you can see it on the call-stack of the exception). SendAsyncCancel therefore will presumably work for SendMailAsync too.
You can't call SendMailAsync from an ASP unless you want to deal with the "Asynchronous operations are not allowed in this context" problem. That's discussed here and looks like it might be messy. Whereas I guess that calling SendAsync probably doesn't have this problem (because I'm using HttpClient.SendAsync and ContinueWith elsewhere, with a CountdownEvent.Wait at the end to wait for the operation to complete, without seeing this exception being thrown).
SendMailAsync can be used when it's invoked via HostingEnvironment.QueueBackgroundWorkItem.

How do I add simple delayed tasks in Django?

I am creating a chatbot and need a solution to send messages to the user in the future after a specific delay. I have my system set up with Nginx, Gunicorn and Django. The idea is that if the bot needs to send the user several messages, it can delay each subsequent message by a certain amount of time before it sends it to seem more 'human'.
However, a simple threading.Timer approach won't work because the user might interrupt this process at any moment prompting future messages to be changed, but the timer threads might not be available to be stopped as they are on a different worker. So far I have come across two solutions:
Use threading.Timer blindly to check a to-send list in the database, can create problems with lots of unneeded threads. Also makes the database less clean/organized.
Use celery or some other system to execute these future tasks. Seems like overkill and over-engineering a simple problem. Tasks will always just be delayed function calls. Also a hassle dealing with which messages belong to which conversation.
What would be the best solution for this problem?
Also, a more generic question:
Ideally the best solution would be a framework where I can 'simulate' a new bot for each conversation so it acts as its own entity and holds all the state/message queue information in memory for itself. It would be necessary for this framework to only allocate resources to a bot when it needs to do something based on a preset delay or incoming message. Is there anything that exists like this?
Personally I would use Celery for this; executing delayed function calls is its job. And I don't know why knowing what messages belong where would be more of a problem there than doing it in a thread.
But you might also want to investigate the new Django-Channels work that Andrew Godwin is doing, since that is intended to support async background tasks.

Famo.us : Is there a way to clear an "emit" emitted by a view?

Currently trying to add multiple functions to a single surface. hoping there is something similar to "pipe" which is "unpipe" is there an "unemit"?
You can removeListener to get rid of the .on or .addListener so that even though the event is emitted it would no longer be acted on. You can also delete what is sending the emit and recreate it without although I don't see that you need to. Even if it never fires again who cares it doesn't take a lot of memory, why would you want it gone?

django-piston: request.data availability within DELETE handlers

django-piston appears to create a data attribute on the request object before it gets to the Handler phase. This data is available, for example, in the PUT and POST handlers by accessing request.data.
However, in the DELETE handler, the data is not available.
I would like to modify django-piston to make this data available but I have no real idea on where to start. Any ideas? Where does the data attribute originate from?
I solved this for myself. The short hacky answer is that the method
translate_mime(request)
from piston.utils needs to be run on the request to make the data attribute available.
The overall fix for this would be to make a change in the Piston source code itself in resource.py to execute the translate_mime method for DELETE actions. Currently it only does to automatically for PUT and POST.
But, like I said, you can actually just manually call translate_mime in the actual handler method and it works fine.