6

I need to send an e-mail when new instance of Entry model is created via admin panel. So in models.py I have:

class Entry(models.Model):   
    attachments = models.ManyToManyField(to=Attachment, blank=True)
    #some other fields
    #...
    sent = models.BooleanField(editable=False, default=False)

Then I'm registring post_save handler function:

def send_message(sender, instance, **kwargs):
    if not instance.sent:
        #sending an e-mail message containing details about related attachments
        #...
        instance.sent = True
        instance.save()

post_save.connect(send_message, sender=Entry)  

It works, but as I mentioned before, I also need to access related attachments to include their details in the message. Unfortunatelly instance.attachments.all() returns empty list inside send_message function even if attachments were actually added.

As I figured out, when the post_save signal is sent, related data of saved model isn't saved yet, so I can't get related attachments from that place. Question is: am I able to accomplish this using signals, or in any other way, or do I have to put this email sending code outside, for example overriding admin panel change view for Entry model?

Dzejkob
  • 2,302
  • 2
  • 15
  • 20

3 Answers3

2

Maybe you could use the M2M Changed Signal instead? This signal is sent when the M2M field is changed.

Evan Porter
  • 2,987
  • 3
  • 32
  • 44
  • Yes I was thinking about it. But how exactly could it be used? As you can see, attachments field have blank=True, so there can be a situation, when there's no related attachments at all, and as I suppose, no m2m_changed signal will be sent then. And if I connect m2m_changed signal to my send_message handler, and there will be more than one attachment added, email message will be (as I think) sent multiple times, which is also wrong. So, any ideas of solving this? – Dzejkob Apr 15 '11 at 20:35
  • I doubt this can be done with signals. You will have to do this with a custom Django app (not the built-in admin). It's a lot of work, but I don't see a way around it. Basically 1) submit form 2) create object + relations based on what was entered and 3) if successful send email. – Evan Porter Apr 15 '11 at 21:06
1

You should be able to do this by overriding the save_model() method on the ModelAdmin. You could either send your email in there or fire a custom signal which triggers your handler to send the email.

If you have inlines, I believe you need to use save_formset() instead.

shadfc
  • 6,104
  • 3
  • 25
  • 19
1

I tried to use ModelAdmin save_model() method, as shadfc proposed. Anyway newly changed related objects aren't accessible from there either. But save_model takes filled form as a parameter, so I used that. My send_message isn't used as a signal handler anymore and I added related_data parameter.

def send_message(sender, instance, related_data={}):
    #sending e-mail using related_data parameter to access additional related objects
    #...

in admin.py I have:

class EntryAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        obj.save()
        send_message(sender=Entry, instance=obj,
                     related_data={'attachments': form.cleaned_data['attachments']} )
Dzejkob
  • 2,302
  • 2
  • 15
  • 20