Django Blog Project #8: Adding basic comment functionality with Django Free Comments
Update 2009-05-08: The notes here apply to Django 0.96 and not
Django 1.0 or later. In particular, FreeComment has been changed
to Comment in Django 1.0. For current documentation, please go
here instead.
This post describes how I added basic commenting functionality using Django's Free Comments. Note, this built-in functionality does not support comment moderation (used to counteract spam) or other nice features. I tried to use David Bennett's comment_utils, but I couldn't get it to work. So for now, I'm just going to use Django Free Comments and hopefully add in moderation and other features later.
I basically followed the instructions on the wiki: Using Django's Free Comments. See there for more information. It was pretty easy to set up.
settings.py
In my settings.py file, I added django.contrib.comments to the
list of installed apps.
~/src/django/myblogsite/settings.py:
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
'django.contrib.comments',
'iwiwdsmi.myblogapp',
)
urls.py
I modified my urls.py to use the comment module's URLConf.
~/src/django/myblogsite/urls.py:
from django.conf.urls.defaults import * from django.contrib.comments.models import FreeComment from iwiwdsmi.myblogapp.views import * from iwiwdsmi.feeds import * feeds = { 'latest': LatestPosts, } urlpatterns = patterns( '', (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': '/home/sofeng/src/django/myblogsite/media'}), (r'^admin/', include('django.contrib.admin.urls')), (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}), (r'^comments/', include('django.contrib.comments.urls.comments')), (r'^myview1/$', myview1), (r'^blog/$', frontpage), (r'^blog/(\d{4})/(\d{2})/([\w\-]+)/$', singlepost), (r'^blog/(\d{4})/$', yearview), (r'^blog/(\d{4})/(\d{2})/$', monthview), (r'^blog/tag/([\w\-]+)/$', tagview), )
python manage.py syncdb
To install the comments model, I ran python manage.py syncdb:
$ cd ~/src/django/myblogsite $ python manage.py syncdb
List page template
Then I modified my list page template to display the number of comments for each post:
~/src/django/myblogsite/templates/listpage.html:
{% extends "base.html" %}
{% load comments %}
{% block title %}
{{ main_title }}
{% if subtitle %}:{% endif %}
{{ subtitle }}
{% endblock %}
{% block header1 %}
{% if subtitle %}
<a href="/blog/">{{ main_title }}</a>
{% else %}
{{ main_title }}
{% endif %}
{% endblock %}
{% block header2 %}
{{ subtitle }}
{% endblock %}
{% block main %}
{% for post in post_list %}
<h3><a href="/blog/{{ post.date_created|date:"Y/m" }}/{{ post.slug }}/">
{{ post.title }}</a>
</h3>
{{ post.body }}
<hr>
<div class="post_footer">
{% ifnotequal post.date_modified.date post.date_created.date %}
Last modified: {{ post.date_modified.date }}<br>
{% endifnotequal %}
Date created: {{ post.date_created.date }}<br>
Tags:
{% for tag in post.get_tag_list %}
<a href="/blog/tag/{{ tag }}/">{{ tag }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
<br>
{% get_free_comment_count for myblogapp.post post.id as comment_count %}
<a href="{{ post.get_absolute_url }}">{{ comment_count }} Comment{{ comment_count|pluralize}}</a>
</div>
<br>
{% endfor %}
{% endblock %}
Oh, I forgot to mention, I implemented a get_absolute_url
method in my Post model. This is the preferred way to
specify the url to the detail view of my Post object.
The code for the method is shown below:
~/src/django/myblogsite/myblogapp/models.py:
import re
from django.db import models
class Post(models.Model):
title = models.CharField(maxlength=200)
slug = models.SlugField(maxlength=100)
date_created = models.DateTimeField() #auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
tags = models.CharField(maxlength=200)
body = models.TextField()
def get_tag_list(self):
return re.split(" ", self.tags)
def get_absolute_url(self):
return "/blog/%d/%02d/%s/" % (self.date_created.year,
self.date_created.month,
self.slug)
def __str__(self):
return self.title
class Meta:
ordering = ["-date_created"]
class Admin:
pass
Detail page template
Then I modified my single post, or detail page, template:
~/src/django/myblogsite/templates/singlepost.html:
{% extends "base.html" %}
{% load comments %}
{% block title %}
{{ main_title }}: {{ post.title }}
{% endblock %}
{% block header1 %}
<a href="/blog/">{{ main_title }}</a>
{% endblock %}
{% block main %}
<h3>{{ post.title }}</h3>
{{ post.body }}
<hr>
<div class="post_footer">
{% ifnotequal post.date_modified.date post.date_created.date %}
Last modified: {{ post.date_modified.date }}<br>
{% endifnotequal %}
Date created: {{ post.date_created.date }}<br>
Tags:
{% for tag in post.get_tag_list %}
<a href="/blog/tag/{{ tag }}/">{{ tag }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</div>
<br>
{% get_free_comment_list for myblogapp.post post.id as comment_list %}
{% get_free_comment_count for myblogapp.post post.id as comment_count %}
{% if comment_list %}
<h4>{{ comment_count }} Comment{{ comment_count|pluralize}}</h4>
{% endif %}
{% for comment in comment_list %}
<a href="#c{{ comment.id }}">#{{ forloop.counter }}</a>
<b>{{ comment.person_name|escape }}</b> commented,
on {{ comment.submit_date|date:"F j, Y" }} at {{ comment.submit_date|date:"P" }}:
{{ comment.comment|escape|urlizetrunc:40|linebreaks }}
{% endfor %}
<br>
<h4>Post a comment</h4>
{% free_comment_form for myblogapp.post post.id %}
{% endblock %}
Create templates for comment preview and comment posted
At this point, I can view these template changes on my blog list view
or detail view. However, if I try to add a comment, I will get a
TemplateDoesNotExist exception. I need two new templates--
one for the comment preview and one for the page just after the comment
is posted. I just copied these from the
wiki. Probably, I should dress these up a little to match my site.
For now, I'll use the generic ones. I put them in a new directory
called comments in my templates directory.
~/src/django/myblogsite/templates/comments/free_preview.html:
<h1>Preview your comment</h1>
<form action="/comments/postfree/" method="post">
{% if comment_form.has_errors %}
<p><strong style="color: red;">Please correct the following errors.</strong></p>
{% else %}
<div class="comment">
{{ comment.comment|escape|urlizetrunc:"40"|linebreaks }}
<p class="date small">Posted by <strong>{{ comment.person_name|escape }}</strong></p>
</div>
<p><input type="submit" name="post" value="Post public comment" /></p>
<h1>Or edit it again</h1>
{% endif %}
{% if comment_form.person_name.errors %}
{{ comment_form.person_name.html_error_list }}
{% endif %}
<p><label for="id_person_name">Your name:</label> {{ comment_form.person_name }}</p>
{% if comment_form.comment.errors %}
{{ comment_form.comment.html_error_list }}
{% endif %}
<p>
<label for="id_comment">Comment:</label>
<br />
{{ comment_form.comment }}
</p>
<input type="hidden" name="options" value="{{ options }}" />
<input type="hidden" name="target" value="{{ target }}" />
<input type="hidden" name="gonzo" value="{{ hash }}" />
<p>
<input type="submit" name="preview" value="Preview revised comment" />
</p>
</form>
~/src/django/myblogsite/templates/comments/posted.html:
<h1>Comment posted successfully</h1>
<p>Thanks for contributing.</p>
{% if object %}
<ul>
<li><a href="{{ object.get_absolute_url }}">View your comment</a></li>
</ul>
{% endif %}
Add some comments
I'm done. I added some comments and saw them show up on my page.
Upload, update, restart server
I uploaded to webfaction, updated my Mercurial repository, and restarted the Apache server. Everything's good.
Here is a snapshot screenshot of a detail page with some comments:
The live site can be viewed at: http://saltycrane.com/blog
Related posts:
Django Blog Project #1: Creating a basic blog
Django Blog Project #2: Deploying at Webfaction
Django Blog Project #3: Using CSS and Template Inheritance
Django Blog Project #4: Adding post metadata
Django Blog Project #5: YUI CSS and serving static media
Django Blog Project #6: Creating standard blog views
Django Blog Project #7: Adding a simple Atom feed
5
Comments
—
Comments feed for this post
#2 nana commented on 2009-05-08:
what happen?? error
File "/home/nanartong/project/myblogsite/../myblogsite/urls.py", line 2, in [HTML_REMOVED] from django.contrib.comments.models import FreeComment
ImportError: cannot import name FreeComment
i use django 1.0.2
help me please
#3 Eliot commented on 2009-05-08:
Ben, I actually haven't set up syndication feeds for comments so I'm not sure about this one.
nana, please see my update at the top of this post.
#5 DSLR-A900 commented on 2011-11-17:
I just book marked your blog on Digg and Stumble Upon. I enjoy reading your commentaries.
Post a comment
About
I'm Eliot and this is my notepad for programming topics such as Python, Django, Ubuntu, Emacs, etc... more »
Search Blog
Tags
-
algorithms
(5)
-
aws
(9)
-
blogproject
(20)
-
c_cplusplus
(12)
-
cardstore
(8)
-
colinux
(2)
-
concurrency
(13)
-
conkeror
(2)
-
core
(2)
-
cygwin
(17)
-
datastructures
(14)
-
datetime
(4)
-
decorators
(4)
-
django
(40)
-
emacs
(22)
-
files_directories
(11)
-
git
(5)
-
hardware
(5)
-
install_setup
(8)
-
javascript
(3)
-
keyboard
(9)
-
matplotlib
(5)
-
mercurial
(4)
-
nginx
(2)
-
persistence
(5)
-
preferences
(7)
-
processes
(4)
-
pyqt
(18)
-
python
(144)
-
ratpoison
(3)
-
regexes
(6)
-
rsync
(3)
-
softwaretools
(17)
-
sql
(14)
-
ssh
(10)
-
subversion
(6)
-
twisted
(7)
-
ubuntu
(65)
-
urxvt
(5)
-
vxworks
(25)
-
webdev
(5)
-
wmii
(7)
Blogroll
- Adam Gomaa
- Alex Clemesha
- Amir Salihefendic
- Armin Ronacher
- David Beazley
- David Ziegler
- Duncan McGreggor
- Gareth Rushgrave
- Glyph Lefkowitz
- Guido van Rossum
- Ian Bicking
- Jacob Kaplan-Moss
- James Bennett
- James Tauber
- Jesper Noehr
- Marty Alchin
- Matt Harrison
- Nikolay Kolev
- Parand Darugar
- Peter Baumgartner
- Peter Bengtsson
- Rob Hudson
- Simon Willison
- Will McGugan
#1 Ben commented on 2009-02-28:
How do you setup comments for syndication feeds?