<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"><channel><title>Holovaty.com</title><link>http://www.holovaty.com/</link><description>Adrian Holovaty's blog.</description><language>en-us</language><lastBuildDate>Tue, 09 Feb 2010 09:27:16 -0000</lastBuildDate><item><title>EveryBlock acquisition and me</title><link>http://www.holovaty.com/writing/everyblock-acquisition/</link><description>&lt;p&gt;I'm excited to announce some huge news. &lt;a href="http://www.everyblock.com/"&gt;EveryBlock&lt;/a&gt;, the project I've led for the last two years, has been acquired by &lt;a href="http://www.msnbc.com/"&gt;MSNBC.com&lt;/a&gt;.

&lt;p&gt;The main details are at the &lt;a href="http://blog.everyblock.com/2009/aug/17/acquisition/"&gt;EveryBlock blog&lt;/a&gt;, but I wanted to mention a few other things here on my personal site, to try to anticipate questions.&lt;/p&gt;

&lt;p&gt;First, this has &lt;em&gt;no impact whatsoever&lt;/em&gt; on my involvement with &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt;. MSNBC.com is half-owned by Microsoft (although it's a separate company), so I expect plenty of Microsoft jokes -- but that's all they are: jokes. I'm not going to start developing things with Microsoft technologies; EveryBlock is not going to be converted from Django/Python to .NET or any other framework, MS or otherwise; I'm going to keep using a Mac as my work and personal computer, although I'm tempted from time to time to return to Linux. All of this is well-established with the MSNBC.com guys; they're cool.&lt;/p&gt;

&lt;p&gt;If anything, this acquisition will give me some &lt;em&gt;more&lt;/em&gt; time to work on Django, because I'm no longer spending a lot of time negotiating, talking with lawyers and worrying about how EveryBlock will continue operation. :-)&lt;/p&gt;

&lt;p&gt;Second, yes, I'm staying in Chicago. The team's main headquarters will continue to be here, except for our coastal EveryBlockers, Wilson Miner (San Francisco) and Paul Smith (Baltimore). I'm hoping I can still participate in the local Web startup scene.&lt;/p&gt;

&lt;p&gt;Third, this has no effect on the EveryBlock open-source code (&lt;a href="http://code.google.com/p/ebcode/"&gt;ebcode&lt;/a&gt;). The code as released on June 30 will continue to be available.&lt;/p&gt;

&lt;p&gt;I'm happy to answer any other questions in the comments. Looking forward to a great next chapter of EveryBlock!&lt;/p&gt;</description><guid>http://www.holovaty.com/writing/everyblock-acquisition/</guid></item><item><title>The definitive, two-part answer to &amp;quot;is data journalism?&amp;quot;</title><link>http://www.holovaty.com/writing/data-is-journalism/</link><description>&lt;p&gt;It's a hot topic among journalists right now: Is data journalism? Is it journalism to publish a raw database? Here, at last, is the definitive, two-part answer:&lt;/p&gt;

&lt;p&gt;1. Who cares?&lt;/p&gt;

&lt;p&gt;2. I hope my competitors waste their time arguing about this as long as possible.&lt;/p&gt;</description><guid>http://www.holovaty.com/writing/data-is-journalism/</guid></item><item><title>Django tip: Caching and two-phased template rendering</title><link>http://www.holovaty.com/writing/django-two-phased-rendering/</link><description>&lt;!-- djangofeed pythonfeed --&gt;
&lt;p&gt;We've &lt;a href="http://blog.everyblock.com/2009/may/18/accounts/"&gt;launched user accounts&lt;/a&gt; at &lt;a href="http://www.everyblock.com/"&gt;EveryBlock&lt;/a&gt;, and we faced the interesting problem of needing to cache entire pages &lt;em&gt;except&lt;/em&gt; for the "You're logged in as &lt;em&gt;[username]&lt;/em&gt;" bit at the top of the page. For example, the &lt;a href="http://chicago.everyblock.com/"&gt;Chicago homepage&lt;/a&gt; takes a nontrivial amount of time to generate and doesn't change often -- which means we want to cache it -- but at the same time, we need to display the dynamic bit in the upper right:&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/2009-05-18_everyblock.png" width="700" height="486" alt="Screenshot of EveryBlock Chicago homepage"&gt;&lt;/p&gt;

&lt;p&gt;One solution would be to pull in the username info dynamically via Ajax. This way, you could cache the entire page and rely on the client to pull in the username bits. The downsides are that it relies on JavaScript and it requires two hits to the application for each page view.&lt;/p&gt;

&lt;p&gt;Another solution would be to use &lt;a href="http://docs.djangoproject.com/en/dev/topics/cache/"&gt;Django's low-level cache API&lt;/a&gt; to cache the results of the queries directly in our view function. The downsides are that it's kind of messy to manage all of that caching, plus each page view still incurs the overhead of template rendering (which isn't horrible, but it's unnecessary overhead).&lt;/p&gt;

&lt;p&gt;The solution we ended up using is &lt;em&gt;two-phased template rendering&lt;/em&gt;. Credit for this concept goes to my friend &lt;a href="http://djangopeople.net/king/"&gt;Honza Kral&lt;/a&gt;, who suggested the idea to me during &lt;a href="http://us.pycon.org/2009/conference/"&gt;PyCon&lt;/a&gt; earlier this year.&lt;/p&gt;

&lt;p&gt;The way it works is to split the page rendering into two steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At cache reset time, render everything &lt;em&gt;except&lt;/em&gt; the "You're logged in as" bit, which should remain unrendered Django template code. Cache the result as a &lt;em&gt;Django template&lt;/em&gt;. (This is the clever part!)&lt;/li&gt;
&lt;li&gt;At page view time, render that cached template by passing it the current user. This is super fast because, at this point, the template only has two or three template tags. (The rest of the page is already rendered.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's a clever solution because you end up defining what &lt;em&gt;doesn't&lt;/em&gt; get cached instead of what &lt;em&gt;does&lt;/em&gt; get cached. It's a sideways way of looking at the problem -- sort of like how Django's template inheritance system defines which parts of the page change instead of defining server-side includes of the common bits.&lt;/p&gt;

&lt;p&gt;In order to make this work, we had to write two parts of infrastructure: a template tag and a middleware class that does the cache-checking and rendering. The template tag looks like this:&lt;/p&gt;

&lt;code&gt;&lt;pre&gt;
# Copyright 2009, EveryBlock
# This code is released under the GPL.

from django import template
register = template.Library()

def raw(parser, token):
    # Whatever is between {% raw %} and {% endraw %} will be preserved as
    # raw, unrendered template code.
    text = []
    parse_until = 'endraw'
    tag_mapping = {
        template.TOKEN_TEXT: ('', ''),
        template.TOKEN_VAR: ('{{', '}}'),
        template.TOKEN_BLOCK: ('{%', '%}'),
        template.TOKEN_COMMENT: ('{#', '#}'),
    }
    # By the time this template tag is called, the template system has already
    # lexed the template into tokens. Here, we loop over the tokens until
    # {% endraw %} and parse them to TextNodes. We have to add the start and
    # end bits (e.g. "{{" for variables) because those have already been
    # stripped off in a previous part of the template-parsing process.
    while parser.tokens:
        token = parser.next_token()
        if token.token_type == template.TOKEN_BLOCK and token.contents == parse_until:
            return template.TextNode(u''.join(text))
        start, end = tag_mapping[token.token_type]
        text.append(u'%s%s%s' % (start, token.contents, end))
    parser.unclosed_block_tag(parse_until)
raw = register.tag(raw)
&lt;/pre&gt;&lt;/code&gt;

&lt;p&gt;This template tag merely treats everything between &lt;code&gt;{% raw %}&lt;/code&gt; and &lt;code&gt;{% endraw %}&lt;/code&gt; as unrendered template code.&lt;/p&gt;

&lt;p&gt;Then, in our base EveryBlock template, we wrap the appropriate bit of code in the &lt;code&gt;{% raw %}&lt;/code&gt; tag, like this:&lt;/p&gt;

&lt;code&gt;&lt;pre&gt;
{% raw %}
	{% if USER %}
		&amp;lt;p&amp;gt;Logged in as {{ USER.email }}&amp;lt;/p&amp;gt;
	{% else %}
		&amp;lt;p&amp;gt;Sign in / register.&amp;lt;/p&amp;gt;
	{% endif %}
{% endraw %}
&lt;/pre&gt;&lt;/code&gt;

&lt;p&gt;The final part is to write some &lt;a href="http://docs.djangoproject.com/en/dev/topics/http/middleware/"&gt;middleware&lt;/a&gt; that renders every &lt;code&gt;text/html&lt;/code&gt; response through the template system:&lt;/p&gt;

&lt;code&gt;&lt;pre&gt;
# Copyright 2009, EveryBlock
# This code is released under the GPL.

from django.core.cache import cache
from django.template import Template
from django.template.context import RequestContext
import urllib

class CachedTemplateMiddleware(object):
    def process_view(self, request, view_func, view_args, view_kwargs):
        response = None
        if request.method == 'GET' and 'magicflag' not in request.GET:
            cache_key = urllib.quote(request.path)
            response = cache.get(cache_key, None)

        if response is None:
            response = view_func(request, *view_args, **view_kwargs)

        if 'magicflag' not in request.GET and response['content-type'].startswith('text/html'):
            t = Template(response.content)
            response.content = t.render(RequestContext(request))

        return response
&lt;/pre&gt;&lt;/code&gt;

&lt;p&gt;One thing to note here is that there's a backdoor for an external process (say, our script that resets the cache) to retrieve the &lt;em&gt;halfway-rendered&lt;/em&gt; template code for any page -- &lt;code&gt;magicflag&lt;/code&gt; in the query string. (We actually use something different on EveryBlock; I've changed this example.) So that means the only thing the cache-resetting script has to do is make a request to the appropriate page, with that query string, and save the result in the cache. Pretty slick.&lt;/p&gt;

&lt;p&gt;There's also a potential gotcha/limitation here: anything within &lt;code&gt;{% raw %}&lt;/code&gt; and &lt;code&gt;{% endraw %}&lt;/code&gt; will only have access to a template context with the default &lt;code&gt;RequestContext&lt;/code&gt; stuff -- which, in our case, will be user-specific stuff.&lt;/p&gt;

&lt;p&gt;Thanks again to Honza for telling me about this concept. It's a great idea, and it's serving us well.&lt;/p&gt;</description><guid>http://www.holovaty.com/writing/django-two-phased-rendering/</guid></item><item><title>Looking toward EveryBlock’s future</title><link>http://www.holovaty.com/writing/everyblock-future/</link><description>&lt;p&gt;It's been a year and a half now since I've started working on &lt;a href="http://www.everyblock.com/"&gt;EveryBlock&lt;/a&gt;, and I'm still having the time of my life. Starting from scratch in July 2007, our team of six has built a one-of-a-kind local news site that now serves 11 cities and makes more than a hundred distinct types of local information useful to people. By all measures, from passionate user feedback to press coverage to traffic numbers to influence on other projects, the site is a success, and we're incredibly proud of our work.&lt;/p&gt;

&lt;p&gt;Thanks to our out-of-the-ordinary funding — a &lt;a href="/writing/knight-foundation-grant/"&gt;generous grant from Knight Foundation&lt;/a&gt; — our team has been given free rein to invent a new form of news, and, more importantly, iterate on the concept. (Check out our recent &lt;a href="http://blog.everyblock.com/2009/jan/23/oneyear/"&gt;one-year anniversary blog post&lt;/a&gt; to see how much we've changed.) This sort of experimentation is sorely needed by the news industry, and we're very lucky to have this opportunity — particularly considering the economy and the trend for news organizations to cut staff rather than invest in innovation.&lt;/p&gt;

&lt;p&gt;But now we've reached an interesting point in our project's growth: our grant ends on June 30, and, under the terms of our grant, we're open-sourcing the EveryBlock publishing system so that anybody will be able to take the code to create similar sites. That's a Good Thing, in that EveryBlock's philosophies and tools will have the opportunity to spread around the world much faster than we could have done on our own, but it puts the six of us EveryBlockers in an odd spot. How do we sustain our project if our code is free to the world?&lt;/p&gt;

&lt;p&gt;We have a number of ideas for sustaining our project beyond a dependency on grants, like building a local advertising engine and/or selling hosted versions of the open-source software, but we're sure there are other ways for EveryBlock to be a successful business. That brings me to the reason I'm posting this — we're looking for ideas and partners who would be interested in helping us figure this out. If you have any ideas or suggestions, &lt;a href="/contact/"&gt;get in touch&lt;/a&gt; with me. I'm confident we'll make something happen; it's just a matter of how.&lt;/p&gt;</description><guid>http://www.holovaty.com/writing/everyblock-future/</guid></item><item><title>Announcing the Django Book, second edition</title><link>http://www.holovaty.com/writing/django-book-second-edition/</link><description>&lt;!--pythonfeed--&gt;&lt;!--djangofeed--&gt;

&lt;p&gt;I'm excited to announce that I'm working on a second edition of the &lt;a href="http://www.djangobook.com/"&gt;Django Book&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first edition, which I cowrote with &lt;a href="http://www.jacobian.org/"&gt;Jacob Kaplan-Moss&lt;/a&gt;, was published in print by Apress more than a year ago, and, sadly, it's become out of date. It covers Django version 0.96, and many of the examples don't work with the current version, 1.0.&lt;/p&gt;

&lt;p&gt;Fortunately, now that Django has reached 1.0 and is committed to backwards compatibility, this book will have a much longer shelf life. :-)&lt;/p&gt;

&lt;p&gt;At this point, I've rewritten/edited the first three chapters and &lt;a href="http://www.djangobook.com/en/2.0/"&gt;published the drafts&lt;/a&gt; for free online, as we did the first time around. I plan to add chapters incrementally over the coming weeks. Instead of posting blog entries here, I'll make those "new chapter posted" announcements on &lt;a href="http://twitter.com/adrianholovaty"&gt;my Twitter account&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What's new in this edition? I'm not only updating examples to work with 1.0; I'm also doing a full rethinking and rewriting of many of the sections and will probably add a couple of chapters (testing Django apps, anyone?).&lt;/p&gt;

&lt;p&gt;As before, we've got a slick granular comment system so that you can leave feedback on particular paragraphs. These were invaluable for the first edition, so I hope folks submit lots of great comments (without, I hope, finding too many errors!).&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.apress.com/"&gt;Apress&lt;/a&gt; has signed on again to publish the second edition in print. Also as before, I'm happy to report that &lt;strong&gt;the book will remain free online&lt;/strong&gt;. The &lt;a href="http://www.djangobook.com/en/1.0/"&gt;first edition is still available in its entirety&lt;/a&gt;, too, but I've added some much-needed caveats that it's out of date.&lt;/p&gt;

&lt;p&gt;Jacob is sitting this one out due to time constraints, so I'll be doing the writing/rewriting, and the fine editors at Apress will be editing. It's easier to rewrite than write from scratch, so it shouldn't take too long.&lt;/p&gt;

&lt;p&gt;Dig in, enjoy, and leave plenty of comments!&lt;/p&gt;</description><guid>http://www.holovaty.com/writing/django-book-second-edition/</guid></item></channel></rss>