0

I have written this signal receiver in django 1.6 that is intended to block posted comments containing bad words from being saved database:

@receiver(pre_save, sender= Comment)
def spam_filter(sender, **kwargs):
    cmnt = kwargs['instance']
    my_expression = '|'.join(re.escape(word) for word in BAD_WORDS)

    if re.search(my_expression, cmnt.body, flags=re.IGNORECASE):    
        #pervent the comment from being saved


    else:
        pass

I am wondering how to tell django in place of '#pervent comment being saved' to not save the 'bad' comment instance?

P.S. the veiw:

@login_required
def add_comment(request, post_id):  

    p= Blog.objects.get(id=post_id)
    post_slug = p.slug
    cform = CommentForm(request.POST)
    if cform.is_valid():
        c = cform.save(commit = False)
        c.created = timezone.now()
        c.post = p
        c.author = request.user
        c.save()
        args=post_slug
        messages.info(request, "comment was added")  
        return HttpResponseRedirect(reverse("Blog.views.post_withslug", 
                                            args=[post_slug]))
supermario
  • 2,625
  • 4
  • 38
  • 58
  • Why have you chosen to use a signal instead of writing this logic in the cleaning of a form field? – Scott Woodall Feb 12 '14 at 21:22
  • @ScottWoodall I'm new to django and you raise a good question. Does using signals instead of integrating into the views has a lot of overhead? If so, I will revise the code. – supermario Feb 12 '14 at 21:49

2 Answers2

4

Based on your comment of being new to Django I'm going to provide an alternative solution. Try looking for the bad words during form validation. You can check specific form fields by writing a method for a specific field by following this format: def clean_<fieldname>. If your CommentForm has a field name body we can do the following:

from django import forms

class CommentForm(forms.Form):
    # Everything as before.
    ...

    def clean_body(self):
        body = self.cleaned_data['body']
        my_expression = '|'.join(re.escape(word) for word in BAD_WORDS)

        if re.search(my_expression, body, flags=re.IGNORECASE):    
            # prevent the comment from being saved
            raise forms.ValidationError("Wash your mouth out with soap.")

        # Always return the cleaned data, whether you have changed it or
        # not.
        return body

Edit:

Some additional comments after seeing your view.

You should be able to add the def clean_body() function mentioned above to your CommentForm and have it behave as expected. Your view as posted will only respond if form.is_valid() evaluates to True. You'll need to make sure the view can handle if someone has a comment that contains a bad word and form.is_valid evaluates to False. See the sample code here for a typical Django function based view. If after fixing these things you're still having problems post the CommentForm and template code along with any exceptions. Be sure to remove your original signal code.

Replace p = Blog.objects.get(id=post_id) with p = get_object_or_404(Blog, pk=post_id). This way if a user supplies a bad post_id they'll immediately get a 404 instead of an exception being thrown as get() will do if it can't find a single object. Docs on get_object_or_404 here.

You can also save a few lines by having your model automatically add the current time when it is created:

class Comment(models.Model):
    created = models.DateField(auto_add_now=True)

Then you can remove c.created = timezone.now() from the view.

Scott Woodall
  • 10,456
  • 6
  • 39
  • 37
  • This looks cleare. The last line should be 'return body'. Also I'd like to print out a message to the user that the comment contained bad word. How can I add this? – supermario Feb 12 '14 at 22:35
  • The line "Wash your mouth out with soap" is returned for the user to see, obviously change it to your liking. Post you view code and I can help further on how to use the above code. – Scott Woodall Feb 12 '14 at 22:40
  • I've updated my post with some additional information. – Scott Woodall Feb 12 '14 at 23:39
1

I think you can raise some exception and catch in

try:
    MyModel.objects.save() #or create
except ValidationError:
    do_other_staff

in your code:

if not re.search(my_expression, cmnt.body, flags=re.IGNORECASE): 
    return True

raise ValidationError('this text explains an error')
Andrey Nelubin
  • 3,084
  • 1
  • 20
  • 25