Theju's tryst with life

Yet another redesign at thejaswi.info

Oh yes!!! You are seeing it right! There's been another redesign of my site.

Technology fascinates me and I cannot get enough of it. This time I wanted my blog powered by the latest and coolest technology. Want to know more?

CouchDB powers the heart of the blogging engine as opposed to an RDBMS. CouchDB-python talks to the DB and the web app. How on earth could I have left my favourite framework out of this cool stuff! I make use of the Django templates and views for the presentation and logic.

CouchDB stores the posts, comments, authentication related data and session data. I have written custom auth and session backends in Django that store the data on the DB. The source has been released and is available here.

The advantages of using couchDB:

  • It scales tremendously. Suppose my blog readership touches a few million, all I would need to do is to add more couchDB nodes and voila it scales!!!
  • Since couchDB uses HTTP as the underlying protocol, it can be cached very easily using standard cache utilities like Squid and Varnish.
  • It is schema-less. I could easily add/edit and remove fields from the DB without having to get frightened about it's integrity.

The disadvantages of using couchDB:

  • couchDB is growing fast and still does not have a few features (slated to be released soon) and the API is a moving target.
  • It can give you a few scares from time to time. I almost lost my data when I made a few cfg changes. After googling around, I figured out that the data was safe but couchDB would require a reinstall.

Hope this time, the technology powering my blog is not obsoleted so fast ;-).

PS1: If you have come to the site from a previous bookmark, please update your bookmarks. I am sorry about breaking your results.

PS2: Some services on my blog are still missing, I promise to have them up at the earliest.

PS3: I am in the process of releasing the source code of the site. If you are one of the impatient folks, please ping me.

I blog in ReSt Part 3

This is the final part in the "I blog in ReSt" series. This part discusses about how to get the templates going for the blog.

  • Create a folder called templates in the project directory and add the absolute path to the directory in the settings.py TEMPLATE_DIRS
  • Create a file called blog.html in templates
    {% load custom_filters %}
    {% load comments %}
    {% load markup %}
    {% load tagging_tags %}

    {% block title %}Title of the Blog{% endblock %}

    {% block content %}
    {% for blog in blog_object_list %}
      {{ blog.blog_title }}
      {{ blog.blog_file|open_file|restructuredtext }}
      Published on {{ blog.blog_date_pub|date:"F j, Y" }}

    {% get_free_comment_list for blog_app.blog_model blog.id as comment_list %}
    {% get_free_comment_count for blog_app.blog_model blog.id as comment_count %}

    {% if comment_count %}

      {% for comment in comment_list %}
         <div class="comment_{% cycle odd,even %}" id="c{{ comment.id }}">
         <span class="comnum"><a id="c{{ comment.id }}"
         href="#c{{ comment.id }}">#{{ forloop.counter }}</a></span>
         <p><strong>{{ comment.person_name|escape }}</strong> commented,
         on {{ comment.submit_date|date:"F j, Y" }} at {{ comment.submit_date|date:"P" }}:</p>
         {{ comment.comment|restructuredtext }}
         </div>
      {% endfor %}

    {% endif %}

    <p>Post a comment</p>

    {% free_comment_form for blog_app.blog_model blog.id %}

   {% endfor %}

   Viewing page <strong>{{ page }} / {{ pages }}</strong>. Total number of posts are {{ hits }}.

   {% if has_previous %}

       {% if year %}

         {% if month %}
           <p><a href="/blog/{{ year }}/{{ month }}/page/{{ previous }}/">Newer Posts</a></p>
         {% else %}
           <p><a href="/blog/{{ year }}/page/{{ previous }}/">Newer Posts</a></p>
         {% endif %}

       {% else %}

         {% if tag %}
           <p><a href="/blog/tag/{{ tag }}/page/{{ previous }}/">Newer Posts</a></p>
         {% else %}
           <p><a href="/blog/page/{{ previous }}/">Newer Posts</a></p>
         {% endif %}

       {% endif %}

     {% endif %}

   {% if has_next %}

      {% if year %}

        {% if month %}
          <p><a href="/blog/{{ year }}/{{ month }}/page/{{ next }}/">Older Posts</a></p>
        {% else %}
          <p><a href="/blog/{{ year }}/page/{{ next }}/">Older Posts</a></p>
        {% endif %}

      {% else %}

        {% if tag %}
          <p><a href="/blog/tag/{{ tag }}/page/{{ next }}/">Older Posts</a></p>
        {% else %}
          <p><a href="/blog/page/{{ next }}/">Older Posts</a></p>
        {% endif %}

      {% endif %}

    {% endif %}
{% endblock %}

You might be wondering what {% load custom_filters %} is. As the name suggests it is a custom filter to read the contents of the file and return them to the restructuredtext filter.

To write your custom filter

  • Create a new application called mytags and add it to the INSTALLED_APPS in settings.py
  • In the mytags directory create a folder called templatetags which must have an __init__.py file and the python file containing your filter code (here it is custom_filters.py).
from django import template

register = template.Library()

def open_file(value):
"Opens the contents of the file given in value."
    try:
        f = open(value,"r")
        contents = f.read()
    except IOError:
        return None
    f.close()
    return contents

# Register the Filter
register.filter(open_file)

The restructuredtext is a filter from django.contrib.markup. The django.contrib.markup currently supports three markup languages Textile, Markdown and ReSt.

Comments are essential for a blog

Django has an in-built comment system that can be activated almost instantly. For activating comments add the django.contrib.comments to your INSTALLED_APPS in settings.py. There are two types of Comments Systems:

  • Comments: For sites that require only registered users to post comments. This system also has the facility to rate comments based on a concept called Karma Score.
  • FreeComments: For sites that require any user to post comments. This is the type of comments one usually places on a blog. But beware spam will soon catch up. For the people who want to take extra precaution, I would recommend James Bennett's (aka Ubernostrum) Django Comment Utils. Django Comment Utils provides lots of features like email on comment, Akismet based spam filtering, easy moderation etc.

The best part about Django's Comment System is that it is extremely easy to use but its biggest drawback is that it hasn't been documented yet. This is no lame excuse for not using Django Comments. In fact lots of users have blogged on how to use the Comment System. Feel free to search and you'll get hold of them. The one link that explains the comment system in detail would be this.

The rest of the code is regarding pagination. Refer here for more details regarding it.

Now you have a blog written in Django that has all the basic features that Blogger has. Also the blog is highly extensible. Extend the blog and let me know of some cool features that you built into your blog.

I blog in ReSt Part 2

Once done with setting up the project, we need to code our views and templates to complete the blog. This part deals with coding the views for the blog. The different views that we require are

  • For displaying the landing page
  • For displaying the blog filtered by year
  • For displaying the blog filtered by month
  • For displaying the blog filtered by day
  • Tags to categorize the blog. For this I make use of Django-Tagging

So let's edit the urls.py file in the project directory to reflect on the urlpatterns.

('^blog/$', get_blog_first_page),
('^blog/(?P<year>\d{4})/$', get_posts_by_year),
('^blog/(?P<year>\d{4})/page/(?P<page>\d)/$', get_blog_page_by_year),
('^blog/(?P<year>\d{4})/(?P<month>\d{2})/$', get_posts_by_month),
('^blog/(?P<year>\d{4})/(?P<month>\d{2})/page/(?P<page>\d)/$', get_blog_page_by_month),
('^blog/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', get_posts_by_day),
('^blog/tag/(?P<tag>[^/]+(?u))/$',tagged_object_list,dict(model=Blog_Model, paginate_by=1, \
allow_empty=True,template_object_name='blog_object', template_name='blog.html',\
extra_context={"tag":1,})),
('^blog/tag/(?P<tag>[^/]+(?u))/page/(?P<page>[0-9]+)/$',tagged_object_list,dict(model=Blog_Meta, \
 paginate_by=1, allow_empty=True, template_object_name='blog_object', template_name='blog.html',)),

By doing so we will be accepting all urls like

/blog/                       # the first page with latest items
/blog/2007/                  # the first page of all blog posts from 2007
/blog/2007/page/4/           # the fourth page of blog posts from 2007
/blog/2007/10/               # blog posts from October 2007
/blog/2007/09/page/2/        # the second page from November 2007
/blog/2007/11/15/            # the post on 15 November 2007
/blog/tag/django/            # Posts with tag django
/blog/tag/django/page/7/     # Seventh page with tag django

Django provides a date-based generic view that saves us from the writing views for date filtering etc. But date-based generic views do not provide pagination by default. But django is powerful and highly extensible. It provides an Object Paginator which can be used for paginations. So I will use a list-based generic view to mimic the date-based generic views with pagination. Open the views.py file in blog_app and paste the following code.

 from django.core.paginator import ObjectPaginator
 from blog_app.models import Blog_Model
 from django.http import HttpResponse, Http404
 from django.template import loader, Context
 from django.views.generic.date_based import archive_day
 from django.views.generic.list_detail import object_list
 import tagging

 # Number of Entries per page (paginate_by)
 num_entries_per_page = 7

 def get_blog_page(request,page):
     queryset = Blog_Model.objects.order_by("-blog_date_pub")
     paginator = ObjectPaginator(queryset, num_entries_per_page)
     cloud_list = tagging.models.Tag.objects.cloud_for_model(Blog_Model,steps=2)
     paginator.pages
     if int(page) in paginator.page_range:
         return object_list(request,queryset,num_entries_per_page,page,template_name='blog.html',\
         template_object_name='blog_object',extra_context={'cloud_list': cloud_list,})
     else:
         raise Http404

 def get_blog_first_page(request):
     return get_blog_page(request,1)

 def get_blog_page_by_year(request,year,page):
     queryset = Blog_Model.objects.filter(**{"blog_date_pub__year":year}).order_by("-blog_date_pub")
     try:
         assert queryset
     except AssertionError:
         raise Http404
     paginator = ObjectPaginator(queryset, num_entries_per_page)
     paginator.pages
     cloud_list = tagging.models.Tag.objects.cloud_for_model(Blog_Model,steps=2)
     if int(page) in paginator.page_range:
         return object_list(request,queryset,num_entries_per_page,page,template_name='blog.html',\
         template_object_name='blog_object',extra_context={"year":year,'cloud_list': cloud_list,})
     else:
         raise Http404

def get_posts_by_year(request, year):
    return get_blog_page_by_year(request,year,1)

def get_posts_by_day(request,year,month,day):
    month_dict = {1:"Jan",2:"Feb",3:"Mar",4:"Apr",5:"May",6:"Jun",7:"Jul",8:"Aug",\
                 9:"Sep",10:"Oct",11:"Nov",12:"Dec"}
    try:
       month_name = month_dict[int(month)]
       except ValueError:
          raise Http404
    cloud_list = tagging.models.Tag.objects.cloud_for_model(Blog_Model,steps=2)
    return archive_day(request,year,month_name,day,Blog_Model.objects.all(),'blog_date_pub',\
    template_name='blog.html',template_object_name='blog_object',\
    extra_context={'cloud_list': cloud_list,})

I have not given the complete view. You can check it out here. The next part in this series talks about templates.

I blog in ReSt Part 1

Nowadays everyone blogs. Blogs have become the medium for people to express almost everything. So I started blogging in January on Blogger. Initially I was satisfied with it but started feeling cramped for features. Wordpress and PyBlosxom looked like good alternatives but it was the latter that impressed me the most. I wanted to blog using PyBlosxom. Then I realized, what's the point in knowing Django and still not writing your own blog app.

My requirements for the blog were:

Once done with the requirements, I started scripting for my blog and here is a step-by-step process of how to go about it. The blog can be built for any markup langauge like Markdown, PyTextile or ReSt.

  • First fire up a django project.
$ django-admin.py startproject myproject
  • Then start a new application called blog_app.
$ python manage.py startapp blog_app
  • Edit the settings.py to add a database engine and a database of your choice. I used SQLite because it is a lightweight database with a very small footprint. Also add django.contrib.markup, django.contrib.admin, blog_app in the INSTALLED_APPS.
  • Open the models.py file in blog_app and create a new Model. The blog will also support tags using Django-Tagging
from django.db import models
from tagging.models import Tag
from tagging.fields import TagField

class Blog_Model(models.Model):
    blog_title      = models.CharField(max_length=250)
    blog_date_pub   = models.DateField()
    blog_file_name  = models.CharField(max_length=250)
    blog_tags       = TagField(max_length=255)

    def save(self):
    # Override the save method of Model.
        super(Blog_Meta, self).save()
        self.tags = self.blog_tags

    def _get_tags(self):
        return Tag.objects.get_for_object(self)

    def _set_tags(self, blog_tags):
        Tag.objects.update_tags(self, blog_tags)

    tags = property(_get_tags, _set_tags)

    def __unicode__(self):
        return self.blog_title
  • Save the above code and run syncdb from the project directory.
$ python manage.py syncdb
  • Uncomment the admin urlpattern from the urls.py file and run the server.
$ python manage.py runserver
  • Go to the admin url in your webbrowser and add a few entries to your Blog Models.

In the next part we see how to write the views and templates for the blog.

The number of posts are 4. The number of pages are 1.