Prerequisites

First off, consider my post to be an extension of this tutorial. That tutorial took me 80% of the way there, but lacked a few things such as deployment and showing all posts for a tag. After you have completed Arun’s tutorial, I will show you some of the tweaks I made. It was hard to find tutorials using both Django 1.7 and Python 3.4, so hopefully this will be of use to people!

Note: I highly recommend using virtualenv to create the project and manage dependencies, it will make deployment much easier in the future. I also suggest that you manage the project with Git so that you don’t need to set it up later on.

Update: This was a fun way to try out Django, but ultimately I ended up migrating my blog over to Pelican.

Dependencies

Make sure to add django_markdown and markdown2 to your INSTALLED_APPS. You will also need to download them to your virtualenv with pip.

Tags

We only need to make a slight modification to our Tag model.

class Tag(models.Model):
    slug = models.SlugField(max_length=200, unique=True)

    def __str__(self):
        return self.slug

    def get_absolute_url(self):
        return reverse("tag_index", kwargs={"slug": self.slug})

This is similar to what we have in our Post model and will allow us to get a URL for a Tag. I renamed Entry to Post in my project. You can read more about get_absolute_url() here. Don’t forget to run your migration after editing a model:

python manage.py makemigrations
python manage.py migrate blog

I also changed post-meta in my templates showing post tags to look like this:

<p class="post-meta">
{{ object.date_created }} tagged under {% for tag in object.tags.all %}<a class="post-category post-category-js" href="{% url "tag_index" slug=tag.slug %}">{{ tag }}</a>{% endfor %}
</p>

Arun’s tutorial was just joining all tags and displaying it in one block. This will loop through a post’s tags and display them separately. I’m also using {% url "tag_index" slug=tag.slug %} to get the URL which we just defined in our model.

Next, we need to edit views.py to control what happens when someone goes to our tag URL:

class TagIndex(ListView):
    template_name = 'home.html'
    paginate_by = 5

    def get_queryset(self):
        slug = self.kwargs['slug']
        tag = Tag.objects.get(slug=slug)
        results = Post.objects.filter(tags=tag)
        return results

I’m re-using my home template because the tag index will do exactly the same thing as my home page, but instead only display a subset of my posts. get_queryset() is what performs the actual filtering based on the tag selected.

Lastly, we need to glue everything together with a route. I added this to my blog’s urls.py file:

    url(r'^tag/(?P<slug>\S+)$', views.TagIndex.as_view(), name="tag_index"),

Be extra careful about the order of your URLs (thanks to Mustafa), as per the docs:

Django runs through each URL pattern, in order, and stops at the first one that matches the requested URL.

Pygments

I’m using pygments for syntax coloring of my code blocks. After a lot of research I chose the markdown2 Python lib to handle this. Change your Post model to look like so:

class Post(models.Model):
    title = models.CharField(max_length=255)
    body = models.TextField()
    body_html = models.TextField(editable=False, blank=True, null=True)
    publish = models.BooleanField(default=False)
    slug = models.SlugField(max_length=200, unique=True)
    date_created = models.DateTimeField(auto_now_add=True)
    date_modified = models.DateTimeField(auto_now=True)
    tags = models.ManyToManyField(Tag)

    objects = PostQuerySet.as_manager()

    def save(self):
        self.body_html = markdown2.markdown(self.body, extras=['fenced-code-blocks'])
        super(Post, self).save()

    def get_absolute_url(self):
        return reverse("post_detail", kwargs={"slug": self.slug})

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "Blog Post"
        verbose_name_plural = "Blog Posts"
        ordering = ['-date_created']

I added the body_html field and the save() method override, which is called when the model is about to be saved to the database. The body_html field isn’t editable in the admin panel, when a post is saved it gets set with the HTML result of calling our markdown2 lib. Doing our pygment rendering this way will be less stressful on the server because it’s only done when a post is saved rather than each time the user views the page.

Don’t forget to perform your migrations!

Since we added our body_html field we need to update our templates to use it. Change your post template to use this for displaying the post body:

<div class="post-content">
    {{ object.body_html|safe }}
</div>

We use it in conjunction with safe for an important reason. Django will correctly sanitize any input before it’s saved to the database. Using safe will convert our escaped HTML back into normal HTML that the browser can then display.

Next up

Those are all the tweaks I added to Arun’s tutorial. You should be able to click on a tag and view all posts for that tag. You should also have syntax highlighting. In part 2 I will go over how to deploy your blog to a service such as DigitalOcean.

Check it out here.