Blogging on App Engine Interlude: Editing and listing

This is part of a series of articles on writing a blogging system on App Engine. An overview of what we're building is here.

A couple of things didn't quite make it into part 2 of the series: Listing and editing posts in the admin interface. This post is a short 'interlude' between the main posts in the series, and briefly covers the changes needed for those features.

Editing posts requires surprisingly little work, thanks to our use of the Django forms library. First, we write a decorator function that we can attach to methods that require an optional post ID, loading the relevant BlogPost object for us:

def with_post(fun):
  def decorate(self, post_id=None):
    post = None
    if post_id:
      post = BlogPost.get_by_id(int(post_id))
      if not post:
        self.error(404)
        return
    fun(self, post)
  return decorate

Then, we enhance the PostHandler to take an optional post ID argument, using the decorator we just defined. Here's the new get() method:

  @with_post
  def get(self, post):
    self.render_form(PostForm(instance=post))

If no post ID is supplied, post is None, and the form works as it used to. If a post ID is supplied, the post variable contains the post to be edited, and the form pre-fills all the relevant information. The same applies to the post() method.

Now all we have to do is add an additional entry for the PostHandler in the webapp mapping:

    ('/admin/post/(\d+)', PostHandler),

Voila - we just converted our 'add post' handler into an 'add or edit' handler, with a minimum of changes! You can see the complete diff here.

Listing posts is extremely simple: First, we refactor the 'render_to_response' method into a BaseHandler, as we suggested in part 2. Then, we create a new AdminHandler. This handler simply fetches a set of posts from the datastore, ordered by publication date, and renders a template listing them. Here's the full code for the AdminHandler, most of which is concerned with providing the Django templates with the correct offsets to use for generating next and previous links and the post count:

class AdminHandler(BaseHandler):
  def get(self):
    offset = int(self.request.get('start', 0))
    count = int(self.request.get('count', 20))
    posts = BlogPost.all().order('-published').fetch(count, offset)
    template_vals = {
        'offset': offset,
        'count': count,
        'last_post': offset + len(posts) - 1,
        'prev_offset': max(0, offset - count),
        'next_offset': offset + count,
        'posts': posts,
    }
    self.render_to_response("index.html", template_vals)

You can see the complete diff needed to add post listing support, including the template for index.html, here.

Finally, Sylvain, from the #appengine IRC channel, pointed out that the blog as it stands doesn't handle unicode gracefully. Fortunately, fixing that is simple - instead of setting the mime type for generated pages to "text/html", we set it to "text/html; charset=utf-8". This simple change is all that's required. You can see the diff for that, along with internationalization improvements to the slugify function, here.

Comments

blog comments powered by Disqus