0

I'm trying to get create_reminder_send_message() executed THE FIRST TIME the Reminder object is saved AND the Reminder.users is saved. The code as it is executes every time I update the object... what am I missing? How can I accomplish what I want?

class Reminder(models.Model):
    METHODS = (
        ('EM', 'Send Email'),
        ('TS', 'Create Dashboard Task'),
        ('ET', 'Both (recommended)')
    )
    info = models.TextField()
    method = models.CharField(max_length=3, choices=METHODS, db_index=True,
                              help_text='''How should I remind the user? (
                              remember that the backend will not be able to
                              send the emails if the users haven't set it up
                              in their profile options)''')
    users = models.ManyToManyField(settings.AUTH_USER_MODEL,
                                   related_name='reminders')
    due_date = models.DateField(blank=True, null=True, db_index=True)
    remind_date = models.DateField(db_index=True)
    sent = models.BooleanField(default=False, db_index=True)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,
                                   related_name='created_by')

def create_reminder_send_message(sender, **kwargs):
    '''
    A signal that creates a new "Message" when a reminder is assigned
    to a user or group of users.
    '''
    instance = kwargs.get('instance')
    text = "I have added a new reminder for you. \nActivation date: {0}".format(instance.remind_date)
    message = Message.objects.create(user=instance.created_by,
                    subject='New reminder!', body=text, draft=False)
    message.to = instance.users.all()
    message.received = timezone.now()
    message.save()


models.signals.m2m_changed.connect(create_reminder_send_message, sender=Reminder.users.through)
la_f0ka
  • 1,773
  • 3
  • 23
  • 44
  • in the signal method, can you not just do a `instance.users.count() == 0` ? – karthikr Jun 28 '13 at 15:53
  • but I need to assign instance.users.all() to message.to – la_f0ka Jun 28 '13 at 16:03
  • i get that, but before that, do a check, and do whatever you want if the condition passes – karthikr Jun 28 '13 at 16:04
  • if the condition passes I don't want to do anything. I want to create the message ONLY after the first time the Reminder object is saved... but I can't use a post_save signal because M2M relationships are added after the object was saved. – la_f0ka Jun 28 '13 at 16:06
  • @PeterDeGlopper I thought about it, but then it would be a pain to manage for anything else... I guess I could keep a Reminder.counter field that updates every time the reminder is saved and only execute the signal callback when instance.counter == 1 – la_f0ka Jun 28 '13 at 16:09
  • Actually, that won't work super well if you add multiple users - an instance of the intermediate model would be created each time, so your signal would get triggered once per user. Hence my deletion of the comment. I guess that might be OK if you don't want bulk email. – Peter DeGlopper Jun 28 '13 at 16:11
  • Hmm. How about explicitly setting the `through` model, and adding a boolean field on that to indicate whether or not that user has already received a notification for that reminder? Then you could do something like `UserReminder.objects.filter(reminder=instance, notification_sent=False).values_list('user__email', flat=True).distinct()` to get your recipients list. Except that won't last if you ever remove users from the reminder, since that boolean would be removed too. And if that never happens, you can just filter on whether the action is `post_add` or not in your signal handler. – Peter DeGlopper Jun 28 '13 at 16:17

1 Answers1

0

Django's m2m_changed signal offers you an action argument. You could check in your signal receiver if the action is pre_add and then check if already a reminder exists. This will work except for the case when all reminders get deleted and a new one gets created - don't know if it's ok for you to execute the code then. Otherwise the only possibility is storing additional data, eg. you could set a boolean to True at the first time or store the instance as well in your Message object, so you can check if a message already exists...

Bernhard Vallant
  • 49,468
  • 20
  • 120
  • 148