Part 2: Django Comments for Authenticated Users

Thu 20 November 2008 by Thejaswi Puthraya

Part 2: Django Comments for Authenticated Users

In the previous part, we had seen how to setup the basic portions of the project. In this part, we are going to build on that and achieve using the comments framework to accept comments from registered users only.

Step - 2: Getting templates ready

The comments are bound to different apps through the use of content_type and object_pk in the templates. If you check up the stock templates in the django.contrib.comments templates directory you will see how the binding is done.

Create a directory called test_app under your templates directory and create 3 files and name them base.html, photo_gallery_list.html and photo_gallery_detail.html.

Contents of base.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Comment demo</title>
    {% block head %}
    {% endblock %}
  </head>
  <body>
    {% block content %}
    {% endblock %}
  </body>
</html>

Step-3: Getting your admin ready to upload photos

Django's admin is one of the umpteen reasons it is so popular. It makes some CRUD part very easy.

Create a new file in test_app called admin.py.

Contents of admin.py

from django.contrib import admin
from test_app.models import Photo_Gallery

class Photo_Gallery_Admin(admin.ModelAdmin):
    list_display = ["name","pic"]

admin.site.register(Photo_Gallery, Photo_Gallery_Admin)

Step-4: Fire up your dev-server

You might have a question if we are done yet...and the answer to that would be a NO!!! We are just firing up the server to check if we have done everything right until here.

Make sure to sync your tables before you fire up your dev-server.

[theju@localhost comments_reg_users]$ python manage.py syncdb
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table django_admin_log
Creating table django_comments
Creating table django_comment_flags
Creating table test_app_photo_gallery

You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (Leave blank to use 'theju'): admin
E-mail address: admin@vce.ac.in
Password:
Password (again):
Superuser created successfully.
Installing index for auth.Permission model
Installing index for auth.Message model
Installing index for admin.LogEntry model
Installing index for comments.Comment model
Installing index for comments.CommentFlag model

and then fire your dev server

[theju@localhost comments_reg_users]$ python manage.py runserver
Validating models...
0 errors found

Django version 1.1 pre-alpha, using settings 'comments_reg_users.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Open your browser and direct it to http://127.0.0.1:8000/admin/. Open the Photo Gallery tab and add a few photos. Open a new tab (don't log out from the admin) on your browser and check out http://127.0.0.1:8000/names/. Then click on any photo object and you'll see the comments form. Log out from the admin tab and refresh this page and you'll see it vanish.

All happy till now??? But you might ask "Does the registered user have to go through the pain of entering his name and email id everytime?" and "What is the guarantee that this form cannot be spoofed?" My reply to these questions would be "Hey, I didn't tell this was done yet!!!"

The answer to the first question would be to override the comments/form.html.

Step-5: Slightly customize your comments form

Edit the base.html under templates/test_app to look like below:

 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 <html lang="en">
   <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
     <title>Comment demo</title>
     <script type="text/javascript">
       function disableReqdInputs(){
         var nameInput = document.getElementById('id_name');
         if (nameInput){
           nameInput.value = "{{ request.user.get_full_name }}";
           nameInput.readOnly=true;
         }
         var emailInput = document.getElementById('id_email');
         if (emailInput){
           emailInput.value = "{{ request.user.email }}";
           emailInput.readOnly=true;
         }
       }
     </script>
    {% block head %}
    {% endblock %}
  </head>
  <body onload="disableReqdInputs();">
  {% block content %}
  {% endblock %}
  </body>
</html>

The above javascript will make the name and email inputs readonly and also autofill the names as mentioned in the django.contrib.auth.

Note

You can also use jQuery to get most of your javascript work done too.

Now we need to answer the second question regarding form-spoofing. For this we need to write a wrapper around the default post_comment that will prevent invalid data or changed data (like name or email).

Step-6: Writing a wrapper around post_comment

When the comment gets posted we need to make sure that it is redirected first to our wrapper.

For this add the following line in the urls.py and make sure this line always comes above the ^comments/ line. The reason is that django's urlresolvers match URLs from top-to-bottom.

(r'^comments/post/', 'test_app.views.comment_post_wrapper'),

Next open the test_app.views file and write the wrapper like below:

from django.contrib.comments.views.comments import post_comment
from django.http import HttpResponse

def comment_post_wrapper(request):
    # Clean the request to prevent form spoofing
    if request.user.is_authenticated():
        if not (request.user.get_full_name() == request.POST['name'] or \
               request.user.email == request.POST['email']):
            return HttpResponse("You registered user...trying to spoof a form...eh?")
        return post_comment(request)
    return HttpResponse("You anonymous cheater...trying to spoof a form?")

That's it, we are done...just test out your application now and it should work. If you don't like your inputs to be readonly, you can always make them hidden by altering the comments/form.html. The wrapper still remains the same.

Conclusions

There is a very interesting ticket #8630 that deals with the customization of comments. The ticket has some nice docs courtesy of Carljm and is slated to be in by Django 1.1.

With that ticket, the inbuilt comments should become much more easier to customize.