Miscellaneous Site Updates

I figured I should probably post something since I haven't done so yet this year. I've been making several changes to the site lately. Most of them are pretty subtle, but I hope they're useful to you. Updates include

  • You can now send an article to some friends by clicking on the "envelope" icon in the top-right corner of each article. It's a pretty simple mechanism.

  • You can save any of my articles to your hard drive for later consumption in the form of a PDF. Just click the "save" button in the top right corner of any article, and you will be able to view/save the latest revision of the respective article as a PDF. One thing to note on this, though, is that the program I use to generate the PDFs does not support the line numbers in code blocks. Sorry folks.

    For those of you who are interested, I'm using rst2pdf to generate the PDFs from my reStructuredText-based articles.

  • I've removed the calendar from the sidebar and replaced it with my newest useless side project: django-bibliophile. It allows me to share my reading progress with my visitors, because I know you all care that much. I plan on officially releasing the project in the near future.

  • Pagination has been implemented in parts of the article archive.

  • I've added an "article distribution" chart when looking at a year's blog articles.

  • Other random improvements.

Announcing django-smileys 0.1.0-rc1

I've released yet another absurdly useless application today. With all of my dirty finals lately, I needed something a little more leisurely to think about. I noticed that I put a lot of those funky smiley codes in my articles and whatnot, so I decided to beautify them a little by replacing the codes with emoticon images a la those silly forum sites I used to be crazy about as a kid.

It was nice to have a good 15-minute breather to work on this. Being such a quick application, I'm sure that there's a lot lacking in it. If you want more features or find a problem with it, just give me a holla and I'll try to update things.

Without any further ado, checkout the project pages:

B-)

Syntax Highlighting, ReST, Pygments, and Django

Some of you regulars out there may have noticed an interesting change in the presentation of some of my articles: source code highlighting. I've been interested in doing this for quite some time, I just never really got around to implementing it until last night.

I found this implementation process to be a bit more complicatd than I had anticipated. For my own benefit as well as for anyone else who wants to do the same thing, I thought I'd document my findings in a thorough article for how to add syntax highlighting to an existing Django- and reStructuredText-powered Web site.

The power behind the syntax highlighting is:

Python is a huge player in this feature because reStructuredText (ReST) was built for Python, Pygments is the source highlighter (written in Python), and Django is written in Python (and my site is powered by Django). Some of you may recall that I converted all of my articles to ReST not too long ago because it suited my needs better than Textile, my previous markup processor. At the time, I was not aware that the conversion to ReST would make it all the easier for me to implement the syntax highlighting, but last night I figured out that that conversion probably saved me a lot of frustration. Cascading Stylesheets (CSS) are responsible for making the source code actually look good, while Pygments takes care of assigning classes to various parts of the designated source code and generating the CSS.

So, the first set of requirements, which I will not document in this article, are that you already have a Django site up and running and that you're familiar with ReST syntax. If you have the django.contrib.flatpages application installed already, you can type up some ReST documents there and apply the concepts discussed in this article.

Next, you should ensure that you have Pygments installed. There are a variety of ways to install this. Perhaps the easiest and most platform-independent method is to use easy_install:

$ easy_install pygments

This command should work essentially the same on Windows, Linux, and Macintosh computers. If you don't have it installed, you can get it from its website. If you're using a Debian-based distribution of Linux, such as Ubuntu, you could do something like this:

$ sudo apt-get install python-pygments

...and it should take care of downloading and installing Pygments. Alternatively, you can download it straight from the PyPI page and install it manually.

Now we need to install the Pygments ReST directive. A ReST directive is basically like a special command to the ReST processor. I think this part was the most difficult aspect of the implementation, simply because I didn't know where to find the Pygments directive or how to write my own. Eventually, I ended up downloading the Pygments-1.0.tar.gz file from PyPI, opening the Pygments-1.0/external/rst-directive.py file from the archive, and copying the stuff in there into a new file within my site.

For my own purposes, I made some small adjustments to the directive over what come with the Pygments distribution. I think it would save us all a lot of hassle if I just copied and pasted the directive, as I currently have it, so you can see it first-hand.

"""
    The Pygments reStructuredText directive
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    This fragment is a Docutils_ 0.4 directive that renders source code
    (to HTML only, currently) via Pygments.

    To use it, adjust the options below and copy the code into a module
    that you import on initialization.  The code then automatically
    registers a ``code-block`` directive that you can use instead of
    normal code blocks like this::

    .. code:: python

            My code goes here.

    If you want to have different code styles, e.g. one with line numbers
    and one without, add formatters with their names in the VARIANTS dict
    below.  You can invoke them instead of the DEFAULT one by using a
    directive option::

    .. code:: python
       :number-lines:

            My code goes here.

    Look at the `directive documentation`_ to get all the gory details.

    .. _Docutils: http://docutils.sf.net/
    .. _directive documentation:
       http://docutils.sourceforge.net/docs/howto/rst-directives.html

    :copyright: 2007 by Georg Brandl.
    :license: BSD, see LICENSE for more details.
"""

# Options
# ~~~~~~~

# Set to True if you want inline CSS styles instead of classes
INLINESTYLES = False

from pygments.formatters import HtmlFormatter

# The default formatter
DEFAULT = HtmlFormatter(noclasses=INLINESTYLES)

# Add name -> formatter pairs for every variant you want to use
VARIANTS = {
    'linenos': HtmlFormatter(noclasses=INLINESTYLES, linenos=True),
}


from docutils import nodes
from docutils.parsers.rst import directives

from pygments import highlight
from pygments.lexers import get_lexer_by_name, TextLexer

def pygments_directive(name, arguments, options, content, lineno,
                       content_offset, block_text, state, state_machine):
    try:
        lexer = get_lexer_by_name(arguments[0])
    except ValueError:
        # no lexer found - use the text one instead of an exception
        lexer = TextLexer()
    # take an arbitrary option if more than one is given
    formatter = options and VARIANTS[options.keys()[0]] or DEFAULT
    parsed = highlight(u'\n'.join(content), lexer, formatter)
    parsed = '<div class="codeblock">%s</div>' % parsed
    return [nodes.raw('', parsed, format='html')]

pygments_directive.arguments = (1, 0, 1)
pygments_directive.content = 1
pygments_directive.options = dict([(key, directives.flag) for key in VARIANTS])

directives.register_directive('code-block', pygments_directive)

I won't explain what that code means, because, quite frankly, I'm still a little hazy on the inner workings of ReST directives myself. Suffice it to say that this snippet allows you to easily highlight blocks of code on ReST-powered pages.

The question now is: where do I put this snippet? As far as I'm aware, this code can be located anywhere so long as it is loaded at one point or another before you start your ReST processing. For the sake of simplicity, I just stuffed it in the __init__.py file of my Django site. This is the __init__.py file that lives in the same directory as manage.py and settings.py. Putting it in that file just makes sure it's loaded each time you start your Django site.

To make Pygments highlight a block of code, all you need to do is something like this:

.. code:: python

    print 'Hello world!'

...which would look like...

print 'Hello world!'

If you have a longer block of code and would like line numbers, use the :number-lines: option:

.. code:: python
    :number-lines:

    for i in range(100):
        print i

...which should look like this...

for i in range(100):
    print i

That's all fine and dandy, but it probably doesn't look like the code is highlighted at all just yet (on your site, not mine). It's just been marked up by Pygments to have some pretty CSS styles applied to it. But how do you know which styles mean what?

Luckily enough, Pygments takes care of generating the CSS files for you as well. There are several attractive styles that come with Pygments. I would recommend going to the Pygments demo to see which one suits you best. You can also roll your own styles, but I haven't braved that yet so I'll leave that for another day.

Once you choose a style (I chose native for Code Koala), you can run the following commands:

$ pygmentize -S native -f html > native.css
$ cp native.css /path/to/site/media/css

(obviously, you'd want to replace native with the name of the style you like the most) Finally, add a line to your HTML templates to load the newly created CSS file. In my case, it's something like this:

<link rel="stylesheet" type="text/css" href="/static/styles/native.css" />

Now you should be able to see nicely-formatted source code on your Web pages (assuming you've already got ReST processing your content).

If you haven't been using ReST to generate nicely-formatted pages, you should make sure a couple of things are in place. First, you must have the django.contrib.markup application installed. Second, your templates should be setup to process ReST markup into HTML. Here's a sample templates/flatpages/default.html:

{% extends 'base.html' %}
{% load markup %}

{% block title %}{{ flatpage.title }}{% endblock %}

{% block content %}
<h2>{{ flatpage.title }}</h2>

{{ flatpage.content|restructuredtext }}
{% endblock %}

So that short template should allow you to use ReST markup for your flatpages, and it should also take care of the magic behind the .. code:: python directive.

I should also note that Pygments can handle a TON of languages. Check out the Pygments demo for a list of languages it knows how to highlight.

I think that about does it. Hopefully this article will help some other poor chap who is currently in the same situation as I was last night, and hopefully it will save you a lot more time than it took me to figure out all this junk. If it looks like I've missed something, or maybe that something needs further clarification, please comment and I'll see what I can do.

Announcing django-axes 0.1.1-rc1

I've released a new version of django-axes this morning. This project allows you to keep track of failed login attempts on your Django-powered sites quickly and easily. It pays attention to the built-in login functions for the Django administration utility as well as the stock django.contrib.auth.views.login method. If a particular user fails to login successfully after 3 tries (this number is customizable), a record is made of the failure for the site admins to review.

This new version addresses what appeared to be related to some recursive function calls interpretting one failed login attempt as much more than that (sometimes more than 100 alleged failed login attempts for a single actual failed login attempt!). I also added a log file for easier access to the stuff that happens when django-axes kicks into action.

For more information, see the following links:

Please comment with any questions, suggestions, etc you have in regards to django-axes!

django-watermark 0.1.5-pre1

I've been making several updates to django-watermark during my more boring classes the past couple days. I have just released a new version for your pleasure.

Changes include:

  • A lot of code refactoring. There was a lot of logic in the watermark filter itself. I decided this logic should be placed elsewhere so as to make the utils.watermark function easier to use outside the realm of the watermark filter.
  • Removed the "parameter precedence" that used to exist between the positioning, tiling, and scaling parameters. Previously, if scale was defined, any values for position and tile were ignored. Similarly, if any value was given for tile, any value for position was ignored. Now you can use all three parameters simultaneously.
  • Added a couple of goodies for positioning and scaling.

I think this is quite a stable release. Please notify me if you find any problems with it!!

django-watermark 0.1.2-pre1

I've added a few updates to django-watermark and figured it was time to announce another release. Updates include:

  • the ability to place watermarks randomly on the target image
  • updated the way relative positioning works. Now, position=50%x50% will center the watermark image on the target image. Previously django-watermark placed the top-left corner of the watermark image in the center of the target image.
  • the ability to convert the watermark image to a transparent greyscale image before applying it to the target image
  • the ability to rotate the watermark image using either a specific number of degrees or usign a random rotation value

Both Google Code and PyPI have been updated with these changes.

New Comment Notification by E-mail

This little trick certainly isn't anything special. Several other folks have posted about how to have Django send out an e-mail automagically when a new comment has been posted on your site. However, a lot of these other posts seem to be pre-Django 1.0. Some groovy changes took place with the signal system slightly before Django 1.0 was released, so I thought I'd share my method of having Django notify me by e-mail when someone posts a comment on my sites.

My code follows:

from django.db.models.signals import post_save
from django.core.mail import mail_admins
from django.contrib.comments.models import Comment

def notify_of_comment(sender, instance, **kwargs):
    message = 'A new comment has been posted.\n'
    message += instance.get_as_text()

    mail_admins('New Comment', message)

post_save.connect(notify_of_comment, sender=Comment)

Nice and simple, right? Right. All this does is it creates a callback function called notify_of_comment and waits for Django to signal that a Comment object has been saved. Any time that signal is intercepted, the callback will send off an e-mail to anyone in the settings.ADMINS list.

You should be able to put such code anywhere in your project so long as that anywhere is loaded when your project starts up. I usually put signal interceptors in an __init__.py file somewhere, though they should work just as well in a models.py or urls.py file.

I realize that the django-comment-utils project is capable of notifying when a comment has been posted, but I didn't want much else from the project. That is why I did it this way ;)

Adding Captcha To Django's Built-in Comments

I recently had a good friend of mine ask for some help in adding a captcha field to his comments form. I gave him some pointers, but before he could put them into action he had to leave for a Thanksgiving roadtrip home. I didn't give much mind to the idea of putting captchas on my own site since it's not all that popular amongst spammers yet. When I woke up this morning, however, I found myself with a few spare minutes to see if my pointers were correct.

Some of the ideas I shared with my friend turned out to not work very well. As I tinkered about trying to get things to work on my own site, I think I came up with a relatively efficient way of doing things.

Installing The Captcha

The captcha field I use is quite simple and effective. I originally got it from http://django.agami.at/media/captcha/, but the project seems to be unmaintained now. Along the road to Django 1.0, some changes were made to the way form fields work, and there is a minor change required in the base code for this captcha field if you want it to work. Alternatively, you can use a copy of the field that I'm currently using.

All you need to do is extract the captcha directory somewhere on your PYTHONPATH. The author recommends putting it in django.contrib, but I usually just place it straight on the PYTHONPATH so all I need to do is from captcha import CaptchaField instead of from django.contrib.captcha import CaptchaField. Minor details...

Adding The Captcha

The first thing you'll want to do after installing the captcha field is add the field itself to your comments form. Instead of subclassing the built-in django.contrib.comments.forms.CommentForm form, I simply decorated the constructor of the form as such:

1
2
3
4
5
6
7
8
9
from django.contrib.comments.forms import CommentForm
from captcha import CaptchaField

def add_captcha(func):
    def wrapped(self, *args, **kwargs):
        func(self, *args, **kwargs)
        self.fields['security_code'] = CaptchaField()
    return wrapped
CommentForm.__init__ = add_captcha(CommentForm.__init__)

This adds a field called security_code to the CommentForm, and it works the same way as if you had done something like this:

1
2
3
4
5
6
7
from django import forms
from captcha import CaptchaField

class MyCommentForm(forms.Form):
    name = forms.CharField()
    ...
    security_code = CaptchaField()

You can put the decorating snippet from above anywhere you'd like so long as the module you put it in is loaded at some point in your project. I usually put this sort of magic in my main urls.py file so it's harder to forget about when I debug things.

Fixing the Form

The first problem with this little trick seems to be that the CaptchaField is rendered as unsafe HTML in the default form.html template in the built-in comments application. That just means that, instead of seeing the captcha, you will see the HTML necessary to render the CaptchaField directly on the page, like this:

<input type="hidden" name="security_code" value="captcha.caZ1SqQ" />
<img src="/static/captchas/caZ1SqQ/0656f09d3974850397dd4c4974f23a35.gif"
 alt="" /><br /><input type="text" name="security_code"
 id="id_security_code" />

To fix that, you can apply the safe filter to the field and make the template look something like this:

{% load comments i18n %}
<form action="{% comment_form_target %}" method="post">
<table>
{% for field in form %}
    {% if field.is_hidden %}
    {{ field }}
    {% else %}
    <tr{% ifequal field.name "honeypot" %} style="display:none;"{% endifequal %}>
        <th>{{ field.label_tag }}:</th>
        <td {% if field.errors %} class="error"{% endif %}>
            {{ field|safe }} &nbsp;
            {% if field.errors %}{{ field.errors|join:"; " }}{% endif %}
        </td>
    </tr>
    {% endif %}
{% endfor %}
</table>
<p class="submit">
    <input type="submit" name="post" class="submit-post"
     value="{% trans "Post" %}" />
    <input type="submit" name="preview" class="submit-preview"
     value="{% trans "Preview" %}" />

</form>

Notice the {{ field|safe }} in there. Also note that I prefer the table layout for the comment form over the default mode. If you change your form template as I have done, you should put the updated copy in your own project's template directory. It belongs in templates/comments/form.html, assuming that your templates directory is called templates. You'll probably also want to check out the preview.html template for the django.contrib.comments application. I changed mine to look like this:

{% extends "comments/base.html" %}
{% load i18n %}

{% block title %}{% trans "Preview your comment" %}{% endblock %}

{% block content %}
    {% load comments %}
    {% if form.errors %}
    <h1>{% blocktrans count form.errors|length as counter %}Please
     correct the error below{% plural %}Please correct the errors below
     {% endblocktrans %}</h1>
    {% else %}
    <h1>{% trans "Preview your comment" %}</h1>
        <blockquote>{{ comment|linebreaks }}</blockquote>

        {% trans "and" %} <input type="submit" name="submit"
         class="submit-post" value="{% trans "Post your comment" %}"
         id="submit" /> {% trans "or make changes" %}:

    {% endif %}

    {% include 'comments/form.html' %}
{% endblock %}

See how I just use the include tag to pull in the comments/form.html template I mentioned above? Saves a lot of typing and potential for problems... If you update the preview.html template, you should save your copy in templates/comments/preview.html, assuming your templates directory is called templates.

Testing It Out

At this point, you should be able to try out your newly installed captcha-fied comments. If it doesn't work, please comment on this article and perhaps we can figure out the problem!

Project Release: django-watermark 0.1.0-pre1

I've found myself in many situations where I have several images that need a watermark applied to them. Applying a watermark to more than 5 images at a time is annoying at best, even if you do have macros to do the mundane stuff for you. I don't like to do things that annoy me.

Recently, I found myself yet again confronted with a "please watermark these images" sort of situation, and I decided to take a more logical approach to the problem: program it. The result of my efforts is called django-watermark and is available on Google Code and PyPI. To be perfectly honest, I actually ganked most of the useful code for this application from a generous soul on the Internet.

This seems to be working fine for me. I think the positioning for watermarks might need more work, but there are several options to hold people over for the time being. Please let me know what your thoughts are.

ReStructured Text

I've been working on updating all of my articles to use ReStructured text formatting these past couple of days. Basically ever since I started using Django on my personal sites, I used Textile formatting to make my articles look pretty, and it treated me well. However, I found myself with a few extra minutes to do some research this weekend, and the topic I happened to research was the difference between ReStructured text, Markdown, and Textile.

After my brief research into these utilities, I decided it would be worth my while to convert all of my articles and other Textile-formatted content to use ReStructured text instead. Most of this is due to personal preferences for certain conventions--very little technical reasoning came into play when I made the decision to switch (so don't ask me why I abandoned one for the other if you expect a technical pro/con list). Suffice it to say that I consider the ReST syntax to be superior to that of Textile.

In the conversion process, I realized that I wrote a whole lot more in my articles than I thought I did! It took much longer than expected, but the work is done now, and all of the articles should be updated. Most of you probably won't be able to tell much of a difference one way or another, but if you do find a problem with the formatting of any of my articles, please notify me!