2

In my DJango application I'd like to implement a system so that when the email field in my jmodel, MyModel is changed, an email is sent to that email address. I have a ModelForm on this Model of mine. How and how should I implement this?

  1. Should I override the save() method of my ModelForm and send the email there?
  2. Should I try and try an trap some kind of model updated signal and send the email there — if so, how? What signal shoudl I be trapping?

What is a good way to check that the email was changed. This itself is a trivial thing to implement but I'd like the code to reside it it's rightful place.

Thanks

Checking if a field was changed: Django: When saving, how can you check if a field has changed?

Community
  • 1
  • 1
Mridang Agarwalla
  • 43,201
  • 71
  • 221
  • 382
  • Option 1 would couple too much behavior to the model - sending an email is a by-product of that event happening, but it's not a behavior the model needs knowledge of. You can use [django-signals](https://docs.djangoproject.com/en/dev/topics/signals/) to create events and set up notification receivers for this kind of behavior. – wkl Oct 28 '11 at 18:41

2 Answers2

2

You can use django-model-changes to do this without an additional database lookup:

from django.db import models
from django.dispatch import receiver
from django_model_changes import ChangesMixin

class MyModel(ChangesMixin, models.Model):
   # your model

@receiver(pre_save, sender=MyModel)
def send_email_if_changed(sender, instance, **kwargs):
    if 'email' in instance.changes():
        # send email
Robert Kajic
  • 8,689
  • 4
  • 44
  • 43
1

I wouldn't follow the advice of the referenced SO question. It got 18 upvotes but it would appear upvotes aren't everything ;). Actually, it's probably just dated info (2009).

It's far better to use a pre_save signal to do this, which requires absolutely zero changes to your model and therefore doesn't have any negative consequences such as the answer your referenced has.

Essentially, in your receiver method, you look up the instance object from the database. Since this is pre-save, the database hasn't been changed yet. As a result, you can then compare instance.some_field with obj.some_field and see if they're different.

@receiver(pre_save, sender=MyModel)
def send_email_if_changed(sender, instance, **kwargs):
    try:
        obj = MyModel.objects.get(pk=instance.pk)
    except MyModel.DoesNotExist:
        pass # It's new, so email hasn't technically changed, but you might want to do something else here.
    else:
        if not obj.email == instance.email: # Email has changed
            # send email
Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • Hi Chris, I was implementing this and had a little issue. Is it possible to send the email to the new email address but not update the email field in the DB i.e. I want to save the old email address back in the DB. (I know that a lot people don't like people asking questions in comments but I felt that this was directly related to what I've been trying to accomplish and therefore I should ask here.) Thanks Chris. – Mridang Agarwalla Oct 31 '11 at 16:14
  • No, I don't think that's possible. Signals don't pass anything back. They just do things based on values they're given. But, the situation you describe seems contrived anyways. Modify your question with information on what you're actually trying to accomplish. – Chris Pratt Oct 31 '11 at 16:17
  • @MridangAgarwalla You can adapt my answer to do what you want: override `save` and call self.changes() to see if the email changed, if it did you can skip calling `super(MyModel, self).save()` – Robert Kajic Oct 12 '13 at 23:46