Send out messages initiated by triggers

I know Ken does not like signals. But I’m at the point in my development that I want to send messages when things are updated to mainly the Shift model:

class Shift(models.Model):
    status = models.ForeignKey('Status', on_delete=models.CASCADE)
    shift_title = models.CharField('Shift title', max_length=255)
    job = models.ForeignKey('Jobs', blank=True, null=True, on_delete=models.CASCADE)
    start_date = models.DateField('Start date', db_index=True)
    start_time = models.TimeField('Start time', db_index=True)
    end_time = models.TimeField('End time')
    site = models.ForeignKey('Site', blank=True, null=True, on_delete=models.CASCADE)
    user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)
    ex_user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE, related_name='exuser')
    user_notes = models.TextField(blank=True, null=True)
    customer_notes = models.TextField(blank=True, null=True)
    admin_notes = models.TextField(blank=True, null=True)
    invoiced = models.IntegerField(blank=True, null=True)
    copied = models.IntegerField(blank=True, null=True)
    source = models.IntegerField(blank=True, null=True)
    update = models.IntegerField(blank=True, null=True)
    history = HistoricalRecords()

    def __str__(self):
        return self.shift_title

EG when status changes from open (id+1) to pending (id=3) I would like to send out an message to the user.

What would be a good approach to achieve this. I suppose in someway I have to set up triggers and add a message to a queue.

I would be happy to get some ideas on this.

It depends on what you mean by “send out a message”.

If you’re talking about sending emails, then regardless of the mechanism used to trigger that process, you may want to use Celery for that. (Sending emails can be a time-consuming process and so you may not want the response to be delayed for the time it takes to send those emails.)

Whether you want to do that depends upon when and how that field gets updated, and whether you can send the emails quickly enough to make the delay acceptable. (Note that signals are processed in-line - they are not a deferred processing mechanism, which means using a signal provides no benefit in this case.)

If your email infrastructure is fast enough, then you could override the save method on the model to include a call to send the email.

But in order to make an appropriate suggestion, we would need to understand all the circumstances under which you would want a message sent, what events would trigger those circumstances (forms being submitted, etc), and what sort of messaging you’re talking about. (i.e., If it is only the status field changing that would cause this message to be sent, my suggestion would likely be a lot different than if you had 20 different fields across 15 models that may all cause emails to be sent.)

Thanks for you thoughts Ken. In the first place i think of these messages:

  1. when a status changes. 50 to 100 e-mails a day.
  2. Update notification. Preferable push messages 500 a day
    I use Postmark as emailprovider. I saw there is a Postmarker API library
    What I need now is advise how to set this up especially how to trigger the events and initiate the messages.
    Thanks in advance for your thoughts.

#1 - In how many different places in your code does this status get changed? Are these changes all submitted through the same form or different forms?

#2 - In how many different places in your code do these notifications get changed?

#3 - Are these notifications sent to one person each, or can one update result in multiple emails being sent?

#4 - Are data changes all being made using APIs that will result in a model’s save method being called?

#5 - If you’re going to send the emails during the time the view that is updating that data is executing, is the extra delay in response acceptable?

You cana still use signal in your model. But to be more effective and avoaid delays like what @KenWhitesell said, you need to

  1. Specify what field to lookup for these notifications.
  2. Call you notification method inside a thread to make this process separate to avoid delays
  3. Each time you update your model you should call .save() and not .update()

update() is converted directly to an SQL statement; it doesn’t call save() on the model instances, and so the pre_save and post_save signals aren’t emitted. If you want your signal receivers to be called, you should loop over the queryset, and for each model instance, make your changes and call save() yourself.

I need to point out that starting a thread from within a view is a very bad idea. It may work 99.99% of the time, but there are situations where it will fail. Your application does not control the process in which it is being run, and so cannot guarantee that the thread will run to completion if it does not complete before the view returns its response.