This is my attempt at ticket #9819 which aims for better documentation to the comments framework. This is my second attempt at writing documentation and I have uploaded a patch to the ticket but wanted some feedback here without spamming the timeline.
{% load comments %}
Next, let us add the number of comments attached to the particular model instance of Post. For this we assume that a context variable object_pk is present which gives the id of the instance of Post.
The usage of the get_comment_count tag is like below:
{% get_comment_count for blog.post object_pk as comment_count %} <p>{{ comment_count }} comments have been posted.</p>
If you have the instance (say entry) of the model (Post) available in the context, then you can refer to it directly:
{% get_comment_count for entry as comment_count %} <p>{{ comment_count }} comments have been posted.</p>
To get a list of comments, we make use of the get_comment_list tag. This tag's usage is very similar to the get_comment_count tag. We need to remember that the get_comment_list returns a list of comments and hence we will have to iterate through them to display them:
{% get_comment_list for blog.post object_pk as comment_list %}
{% for comment in comment_list %}
<p>Posted by: {{ comment.user_name }} on {{ comment.submit_date }}</p>
...
<p>Comment: {{ comment.comment }}</p>
...
{% endfor %}
Finally, we display the comment form, enabling users to enter their comments. There are two ways of doing so. The first is when you want to display the comments template available under your comments/form.html. The other method gives you a chance to customize the form.
The first method makes use of the render_comment_form tag. It's usage too is similar to the other two tags we have discussed above:
{% render_comment_form for entry %}
It looks for the form.html under the following directories (for our example):
comments/blog/post/form.html comments/blog/form.html comments/form.html
evaluates to /comments/post/. We use this tag in the form's action attribute. The get_comment_form tag renders a form for a model instance by creating a context variable. One can iterate over the form object to get individual fields. This gives you fine-grain control over the form:
{% for field in form %}
{% ifequal field.name "comment" %}
<!-- Customize the "comment" field, say, make CSS changes -->
...
{% endfor %}
But let's look at a simple example:
{% get_comment_form for entry as form %} <!-- A context variable called form is created with the necessary hidden fields, timestamps and security hashes --> <table> <form action="{% comment_form_target %}" method="POST"> {{ form }} <tr> <td></td> <td><input type="submit" name="preview" class="submit-post" value="Preview"></td> </tr> </form> </table>
If you want your users to be able to flag comments (say for profanity), you can just direct them (by placing a link in your comment list) to /flag/{{ comment.id }}/. Similarly, a user with requisite permissions ("Can moderate comments") can approve and delete comments. This can also be done through the admin as you'll see later. You might also want to customize the following templates:
- flag.html
- flagged.html
- approve.html
- approved.html
- delete.html
- deleted.html
found under the directory structure we saw for form.html.
Suppose you want to export a feed of the latest comments, you can use the in-built LatestCommentFeed. Just enable it in your project's urls.py:
from django.conf.urls.defaults import * from django.contrib.comments.feeds import LatestCommentFeed feeds = { 'latest': LatestCommentFeed, } urlpatterns = patterns('', # ... (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}), # ... )
Now you should have the latest comment feeds being served off /feeds/latest/.
Now that we have the comments framework working, we might want to have some moderation setup to administer the comments. The comments framework comes in-built with generic comment moderation. The comment moderation has the following features (all of which or only certain can be enabled):
- Enable comments for a particular model instance.
- Close comments after a particular (user-defined) number of days.
- Email new comments to the site-staff.
To enable comment moderation, we subclass the CommentModerator and register it with the moderation features we want. Let us suppose we want to close comments after 7 days of posting and also send out an email to the site staff. In blog/models.py, we register a comment moderator in the following way:
from django.contrib.comments.moderation import CommentModerator, moderator from django.db import models class Post(models.Model): title = models.CharField(max_length = 255) content = models.TextField() posted_date = models.DateTimeField() class PostModerator(CommentModerator): email_notification = True auto_close_field = 'posted_date' # Close the comments after 7 days. close_after = 7 moderator.register(Post, PostModerator)
The generic comment moderation also has the facility to remove comments. These comments can then be moderated by any user who has access to the admin site and the Can moderate comments permission (can be set under the Users page in the admin).
The moderator can Flag, Approve or Remove comments using the Action drop-down in the admin under the Comments page.
Note
Only a super-user will be able to delete comments from the database. Remove Comments only sets the is_public attribute to False.
This app had been on my todo list for a long time. Today, no matter how much I tried to procrastinate, I could not ignore it and hence decided to scratch my itch and get it done. I swore I wouldn't even get up for a glass of water until it was done. Luckily, this took lesser time than I anticipated and I was spared of breaking my resolve!
There are certain sites that require only authenticated users to post comments, this app specifically targets such cases.
It builds on my previous posts sans the javascript hack. To make the code very condense and easy to use, it uses "Two-phased template rendering" that I saw in Ella via Adrian Holovaty's post. From a security point, it auto-escapes django template code injected in through the comments in the middleware.
TEMPLATE_CONTEXT_PROCESSORS=(
"django.core.context_processors.auth",
"django.core.context_processors.request",
"django.core.context_processors.media"
)
(r'^comment/', include('django.contrib.comments.urls')),
{% load auth_comments %}
...
{% render_auth_comment_form for app.model object_pk %}
This code is equivalent to
{% if request.user.is_authenticated %}
{% render_comment_form for app.model object_pk %}
{% else %}
Some standard message to be shown if user not logged in.
{% endif %}
The standard message is picked up from the DEFAULT_UNAUTH_COMMENT_MESSAGE attribute of settings.py which you can set to override the default message.
If you liked the app, do let me know. Also please fork and improve the code if you wish to improve it.
Very often we post comments on many sites and it becomes very tedious to keep track of discussion thereafter. To make life easy, the site owners need to provide a "Follow-up" feature which would send an email whenever a comment is posted. From the user perspective, this option should be optional ie he should receive the email followups only if he subscribes to them.
So yesterday, I tried my hand at one such reusable app and it turned out to be easier than I thought.
Here's how to go about using it:
Fetch the source from here.
Add the COMMENTS_APP attribute to settings.py to reference followup_comments.
Add a few additional attributes to the settings.py namely
- EMAIL_HOST: If your SMTP server is on a different machine
- EMAIL_PORT: If your port is different than a default
- EMAIL_HOST_USER: If authentication is enabled on SMTP server
- EMAIL_HOST_PASSWORD: If authentication is enabled
- DEFAULT_FROM_EMAIL: If you want to specify the from address
Go to the admin page and add a Followup Message for the site of your choice.
Then use the comments just like you would have used the vanilla comments framework.
Since I have worked on this app for a couple of hours, it still has some rough edges to it. A couple that I could find out were:
Please give me your feedback on how you like this project.
This week I am releasing code for yet another reusable app and yet another threaded-comments app. There are quite a few threaded comment apps around but the best one being this by Eric Florenzano.
The motive of releasing this code is not to compete with any of the apps available but as a proof of how portable django and it's apps (here I am referring to the comments app, but the statement holds true for most third-party apps) can be.
Probably with some work this app should be as good as other apps.
Download the source from here and place the threaded_comments app in your django project.
Patch your django source with the latest patch from #8630.
Add the following attributes in the settings.py.
COMMENTS_APP = "threaded_comments"Make use of the new comment tags that are available to help with the threaded support. Here's an example template:
{% extends "test_app/base.html" %} {% block head %} <script type="text/javascript"> function showCommentForm(id) { var commentForm = document.getElementById('commentForm'+id); var linkHolder = document.getElementById('linkReply'+id); if (commentForm.style.display == "none") { commentForm.style.display = "block"; linkHolder.innerHTML="Hide Reply"; } else { commentForm.style.display = "none"; linkHolder.innerHTML="Reply"; } } </script> {% endblock %} {% block content %} {% load threaded_comments %} {% get_comment_list for app.model object.pk as comment_list order_by thread %} {# To order comments by date use order_by date #} {% if comment_list %} <h2>Comments</h2> {% for comment in comment_list %} <div id="c{{ comment.id }}" style="margin-left: {{ comment.level }}0px;"> <h4>{{ comment.name|escape }} {% if comment.parent %} in reply to <a href="#c{{ comment.parent_id }}">{{ comment.parent_id }}</a> {% endif %} at {{ comment.submit_date|date:"r"}}</h4> <p>{{ comment.comment|escape|urlizetrunc:"100"|linebreaks }}</p> <p><a id="linkReply{{ comment.id }}" href="#" onclick="showCommentForm({{ comment.id }});">Reply</a></p> <div id="commentForm{{ comment.id }}" style="display: none"> <h4>Reply to the above comment ({{ comment.id }})</h4> {% render_comment_form for app.model object.pk with comment.id %} {# Alternatively use #} {# {% get_comment_form for app.model object.pk as form with parent_id %} #} </div> </div> <hr> {% endfor %} {% endif %} <h2>Leave a comment</h2> {% render_comment_form for app.model object.pk %} {% endblock %}Most of the code is unchanged except for the comment tags which now take additional arguments and some optional javascript.
As mentioned in my previous post, the toughest decision is to patch Django because people don't like to pollute their code bases (don't even ask about organizations, they get hysterical when you want to patch any code base).
Please let me know how you find this app and suggest ways to make it DRYer and better.
One aim of Jacob's code for the comments framework in Django 1.0 was to make it as extensible as possible. At the same time this process should be quite generic and as DRY as possible.
It is here that I worked for a very long time but in last minute goof-ups could not push these changes to be included in 1.0.
Well anyways, I opened a ticket quick enough so that this feature didn't fade off with time. I attached an initial patch and soon there was a lot of activity and within no time people contributed tests and docs for the patches. I want to thank all those people who contributed or are interested in the ticket. Luckily this ticket will be in trunk by 1.1 championed by Jacob himself (See roadmap).
So in this blog post I announce a reusable app called recaptcha-django-comments It is a very simple app that appends a Recaptcha field to the comment form.
Fetch the source from here and place the recaptcha_comments app in your django project.
Patch your django source with the latest patch from #8630.
Update the INSTALLED_APPS in the settings.py to reflect this app.
Add the following attributes in the settings.py.
COMMENTS_APP = "recaptcha_comments" RECAPTCHA_PUBLIC_KEY = "xyz....." RECAPTCHA_PRIVATE_KEY = "abc...."Add the following middleware to the settings.py. It will help pass on the IP Address to recaptcha if you are behind a reverse proxy. This step is not required if you are not behind a reverse proxy.
MIDDLEWARE_CLASSES= ('...', 'django.middleware.http.SetRemoteAddrFromForwardedFor', )Copy the comments/ directory from recaptcha_comments/templates into your templates directory as referenced by your settings.py.
Edit the project's urls.py to have the following line.
(r'^comments/', include('recaptcha_comments.urls')),
That's it!!!
Well, the toughest part in the procedure is to patch the django source (by difficult I mean the decision to patch django).
Please let me know your opinions about this app.
Stay tuned for more and oops I forgot...**HAPPY NEW YEAR**!!!