Reputation: 381
I want to send notification emails to a list of users defined in a 'observers' ManyToMany field when a new post is created.
The post is created without errors and the list of observer users are added to it successfully (they appear in the post_detail.html template), but the notification email is never sent to the observer users.
I think I'm doing something wrong in the new_post function below, which I adapted from this code for sending email when a user comments on a post, which does work. Any help much appreciated.
from django.db import models
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.db.models import signals
from notification import models as notification
class Post(models.Model):
author = models.ForeignKey(User, related_name="added_posts")
observers = models.ManyToManyField(User, verbose_name=_("Observers"), related_name='observers+', blank=True, null=True)
# send notification to Post observers
def create_notice_types(app, created_models, verbosity, **kwargs):
notification.create_notice_type("new_post", "New post created", "A new post has been created")
signals.post_syncdb.connect(create_notice_types, sender=notification)
def new_post(sender, instance, created, **kwargs):
context = {
'observer': instance,
'site': Site.objects.get_current(),
}
recipients = []
pk=instance._get_pk_val()
for observer in instance.observers.all().distinct():
if observer.user not in recipients:
recipients.append(observer.user)
notification.send(recipients, 'new_post', context)
signals.post_save.connect(
new_post, sender=models.get_model(
'blog', 'Post'), dispatch_uid="pkobservers")
@login_required
def add(request, form_class=PostForm, template_name="blog/post_add.html"):
post_form = form_class(request)
if request.method == "POST" and post_form.is_valid():
post = post_form.save(commit=False)
post.author = request.user
post_form.save()
post_form.save_m2m()
return redirect("blog_user_post_detail",
username=request.user.username, slug=post.slug)
return render_to_response(template_name,
{"post_form": post_form}, context_instance=RequestContext(request))
Also tried this (after dropping the blog_post and blog_post_observers tables, and running manage.py syncdb
again, but still doesn't work):
class Post(models.Model):
# ....
observers = models.ManyToManyField(User, verbose_name=_("Observers"), related_name='observers+')
def new_post(sender, instance, created, **kwargs):
context = {
'observer': instance,
'site': Site.objects.get_current(),
}
recipients = instance.observers.all()
pk=instance._get_pk_val()
notification.send(recipients, 'new_post', context)
signals.post_save.connect(new_post, sender=models.get_model('blog', 'Post'), dispatch_uid="pkobservers")
When I edit/update a post, using the following view, the notification email does work:
@login_required
def edit(request, id, form_class=PostForm, template_name="blog/post_edit.html"):
post = get_object_or_404(Post, id=id)
if post.author != request.user:
request.user.message_set.create(message="You can't edit items that aren't yours")
return redirect("desk")
post_form = form_class(request, instance=post)
if request.method == "POST" and post_form.is_valid():
post = post_form.save(commit=False)
post.updated_at = datetime.now()
post_form.save()
post_form.save_m2m()
messages.add_message(request, messages.SUCCESS, message=_("Successfully updated post '%s'") % post.title)
return redirect("blog_user_post_detail", username=request.user.username, slug=post.slug)
return render_to_response(template_name, {"post_form": post_form, "post": post}, context_instance=RequestContext(request))
Upvotes: 0
Views: 582
Reputation: 381
Figured it out — the notification email is now sent to observers when creating a new post.
Basically I needed to call save a second time after setting the post.id:
@login_required
def add(request, form_class=PostForm, template_name="blog/post_add.html"):
post_form = form_class(request)
if request.method == "POST" and post_form.is_valid():
post = post_form.save(commit=False)
post.author = request.user
post_form.save()
post.id = post.id # set post id
post_form.save() # save a second time for notifications to be sent to observers
return redirect("blog_user_post_detail",
username=request.user.username, slug=post.slug)
return render_to_response(template_name,
{"post_form": post_form}, context_instance=RequestContext(request))
The models.py is unchanged from the first EDIT: in the question (Thanks to J. C. Leitão for the help).
Here it is to be sure:
class Post(models.Model):
observers = models.ManyToManyField(User, verbose_name=_("Observers"), related_name='observers+')
def new_post(sender, instance, created, **kwargs):
context = {
'observer': instance,
'site': Site.objects.get_current(),
}
recipients = instance.observers.all()
pk=instance._get_pk_val()
notification.send(recipients, 'new_post', context)
signals.post_save.connect(new_post, sender=models.get_model('blog', 'Post'), dispatch_uid="pkobservers")
Upvotes: 0
Reputation: 20133
First of all, I think your many to many relationship should not have blank or null, since each observer would ideally be a user and not a None. This avoids trying to send emails to None users, which probably gives error.
Second, I think you can just use
recipients = instance.observers.all().distinct()
instead of looping on the list (distinct() already considers only unique users)
Third, I don't see why you really need a "distinct()": can a user be observer more than once?
recipients = instance.observers.all()
Fourth, In your code, you are looping trough instance.observers.all() with an "observer", which is already a User. Why do you append observer.user in recipients? I think appending observer should be enough.
Finally, confirm that recipients is not empty. If you have tested notification.send(), your code seems correct.
Upvotes: 2