blog/publish.py
18 January 2008
18:12
as NumPy
The main controller/view stuff running behind my updated blog
| 1 | from __future__ import division |
|---|---|
| 2 | |
| 3 | from datetime import datetime |
| 4 | from itertools import groupby |
| 5 | from math import ceil |
| 6 | from operator import attrgetter |
| 7 | |
| 8 | from webapp import app |
| 9 | from webapp.http import abort |
| 10 | from webapp.routing import redirect_to |
| 11 | from webapp.templating import output, render |
| 12 | |
| 13 | from blog.model import Post |
| 14 | |
| 15 | |
| 16 | @output('index.html') |
| 17 | def index(request, response): |
| 18 | return render( |
| 19 | posts = list(Post.by_time(descending=True, count=10)) |
| 20 | ) |
| 21 | |
| 22 | @output('index.atom', mimetype='application/atom+xml') |
| 23 | def feed(request, response): |
| 24 | page = int(request.GET.get('p', '1')) |
| 25 | offset = (page - 1) * 10 |
| 26 | results = Post.by_time(descending=True, count=10, skip=offset) |
| 27 | if not len(results): |
| 28 | abort(404) |
| 29 | return render( |
| 30 | posts = list(results), |
| 31 | num_pages = int(ceil(results.total_rows / 10)), |
| 32 | page = page, |
| 33 | per_page = 10, |
| 34 | ) |
| 35 | |
| 36 | @output('post.html') |
| 37 | def post(request, response, year, month, slug): |
| 38 | posts = list(Post.by_slug()[(year, month, slug)]) or abort(404) |
| 39 | post = posts[0] |
| 40 | prev = list(Post.by_time(count=-2, startkey_docid=post.id) |
| 41 | [[post.published.isoformat()]:]) |
| 42 | next = list(Post.by_time(count=2, startkey_docid=post.id) |
| 43 | [[post.published.isoformat()]:]) |
| 44 | return render( |
| 45 | post = post, |
| 46 | prev = len(prev) > 1 and prev[0] or None, |
| 47 | next = len(next) > 1 and next[-1] or None |
| 48 | ) |
| 49 | |
| 50 | @output('archives.html') |
| 51 | def archives(request, response): |
| 52 | if 'tag' in request.POST: |
| 53 | redirect_to(tag, request.POST['tag']) |
| 54 | return render( |
| 55 | months = Post.all_months(), |
| 56 | tags = Post.all_tags() |
| 57 | ) |
| 58 | |
| 59 | @output('tag.html') |
| 60 | def tag(request, response, tag): |
| 61 | return render( |
| 62 | tag = tag, |
| 63 | tags = Post.all_tags(), |
| 64 | posts = list(Post.by_tag()[[tag]:[tag, "9999"]]), |
| 65 | ) |
| 66 | |
| 67 | @output('tag.atom', mimetype='application/atom+xml') |
| 68 | def tag_feed(request, response, tag): |
| 69 | page = int(request.GET.get('p', '1')) |
| 70 | offset = (page - 1) * 10 |
| 71 | results = Post.by_tag(descending=True, count=10, skip=offset) \ |
| 72 | [[tag, "9999"]:[tag]] |
| 73 | if not len(results): |
| 74 | abort(404) |
| 75 | return render( |
| 76 | tag = tag, |
| 77 | posts = list(results), |
| 78 | num_pages = int(ceil(results.total_rows / 10)), |
| 79 | page = page, |
| 80 | per_page = 10, |
| 81 | ) |
| 82 | |
| 83 | @output('month.html') |
| 84 | def month(request, response, year, month): |
| 85 | posts = [] |
| 86 | prev = next = None |
| 87 | for year_month, lst in groupby(Post.by_month(), |
| 88 | lambda x: (x.published.year, |
| 89 | x.published.month)): |
| 90 | if year_month == (year, month): |
| 91 | posts = list(lst) |
| 92 | elif not posts: |
| 93 | prev = year_month |
| 94 | else: |
| 95 | next = year_month |
| 96 | break |
| 97 | return render( |
| 98 | posts = posts, |
| 99 | month = datetime(year, month, 1), |
| 100 | prev = prev and datetime(prev[0], prev[1], 1) or None, |
| 101 | next = next and datetime(next[0], next[1], 1) or None |
| 102 | ) |
| 103 | |
| 104 | @output('sitemap.xml') |
| 105 | def sitemap(request, response): |
| 106 | return render( |
| 107 | posts = list(Post.by_time(descending=True)), |
| 108 | months = Post.all_months(), |
| 109 | tags = Post.all_tags() |
| 110 | ) |
Comments
-
Kevin Dangoor says:
21 January 2008
02:36Interesting. Why do you return render(...) vs. the TurboGearsian dict()? Does render() augment the context with other stuff?
-
cmlenz says:
21 January 2008
22:29render() returns a Genshi stream, which is potentially further processed by response/stream filters (most importantly the HTML form filler), and finally gets serialized. A function can also return a plain string or string iterable which won't be passed through Genshi. This design feels a bit cleaner / more explicit to me, but granted it's subtle and probably subjective. One thing this pattern allows (which isn't shown above) is to specify a different template name than the one in the decorator by passing it as the first positional argument. Thus, for functions that can render using different templates, you omit the template name from the @output decorator and just pass it into render().
-
Jason Davies says:
5 March 2008
01:57This looks really cool. I'd be interested to see the code for the webapp stuff too. I'm going to experiment with converting my blog to use CouchDB (currently I used Django + PostgreSQL) soon.
