blog/publish.py

18 January 2008
18:12

By cmlenz 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 )

Download Raw Source

Comments

  1. Kevin Dangoor says:

    21 January 2008
    02:36

    Interesting. Why do you return render(...) vs. the TurboGearsian dict()? Does render() augment the context with other stuff?
  2. cmlenz says:

    21 January 2008
    22:29

    render() 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().
  3. Jason Davies says:

    5 March 2008
    01:57

    This 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.