11

Consider the following:

class OrderForm(models.Model):
    title = models.CharField(max_length=100)
    desc  = models.TextField()


class OrderFormLine(models.Model):
    order = models.ForeignKey(OrderForm)
    lagel = models.CharField(max_length=100)
    qty   = models.IntegerField(...)
    price = models.FloatField(...)

Now I want to send an email with the orderform details whenever someone creates one or modify one.

No rocket science so far .. let's just use a post_save signal;

post_save.connect(email_notify, sender=OrderForm)

But there's one tiny problem, the OrderForm object passed to email_notify is updated with the new data as expected, but not the related OrderFormLine items.

I've tried to override the save methods in the admin AND in the model, I've tried to save the object, the form and its relation before passing it to my notification handler, nothing works.

I'm aware that I could attach the post_save signal to the OrderItem model, but then the email would be sent for each items.

Help I'm on the brink of madness.

UPDATE:

Found a simple and reliable solution

Short story:

def email_notify_orderform(sender, **kwargs):
    instance = kwargs['instance']
    ct = ContentType.objects.get_for_model(OrderForm)
    if ct.id == instance.content_type.id:
        print instance.is_addition()
        print instance.is_change()
        print instance.is_deletion()
        print instance.change_message
        print instance.action_time
        print instance.get_edited_object().total() # BINGO !
post_save.connect(email_notify_orderform, sender=LogEntry)
h3.
  • 10,688
  • 15
  • 51
  • 54
  • 1
    You have to save the order form in order to have a valid ID number to associate with your Line items. Seems to me that a custom signal is in order; that might save you from your madness. Sometimes, special cases are not evil, and this is a very small special case. "OrderForm" signals when it is fully instantiated, and your listener waits for that instead of post_save. – Elf Sternberg Nov 06 '10 at 03:41
  • I never heard about writing custom signals .. and I can't find much about this subject. From what I understand signals are tightly coupled in django and there is no mechanism in place to provide custom signals. Is it documented ? – h3. Nov 06 '10 at 03:52
  • nevermind, found the doc – h3. Nov 06 '10 at 03:53
  • Spent the night on this problem .. it doesn't really solve my problem since I can't attach the signal at the right step in the save process. – h3. Nov 06 '10 at 13:50
  • @h3: All of those cases you post in your blog happens before the inlines are saved? Maybe they are saved, but you are still using an `OrderForm` instance that hasn't been added any related object. – Armando Pérez Marqués Nov 07 '10 at 06:21
  • If you found your solution, you should post it as an answer to your own question, and accept it :). This will help people who have this issue in the future. – TM. Mar 01 '11 at 06:13
  • Using `LogEntry()` was very clever. This Q/A and your blog post helped me out immensely. Thanks! – tatlar Jun 21 '13 at 21:22

1 Answers1

6

The basic problem is that when the main objects post_save signal is sent, the inlines have not been saved yet: the parent model always gets saved first. So, it's not that it's sending old data; in fact it's the current state of the data.

The simplest solution is to create a custom signal and have that signal sent at a place where the inlines have been saved. The save_formset method on ModelAdmin is your hook.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • Unless there's something I don't get about your answer, it does not work. You can see in the blog post all the methods I've tried, it include using save_formset: http://haineault.com/blog/141/ – h3. May 12 '11 at 12:34
  • @h3 Have you gotten the idea? It's been a long time but you can check this [post](http://stackoverflow.com/questions/14858559/save-the-related-objects-before-the-actual-object-being-edited-on-django-admin) and its solutions that illustrate the use of [save_formset](https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_formset). I Left the comment in case someone bumps to this post – raratiru Sep 14 '16 at 22:44