Software Design Paralysis
Friends, I have come to an impasse.
I have an awesome concept for embedding images into these blog posts. The idea is this:
- Use an Attachment model with a generic foreign key to allow images to be attached to blog posts
- Use a markdown extension to allow attached files to be referenced inside the blog post's body
Here's the syntax I wanted to support in markdown:
This isn't that impossible a task, and in fact, I have done it, but there are some wrinkles. The main issue is that I really want to limit my markdown extension's file lookup to only those files actually attached to the blog post. The second issue is that I would like to be able to specify a template to use for rendering each attachment, and I'd like to be able to specify different templates in different contexts. Both problems revolve around how I should do the actual conversion from markdown syntax to HTML.
I implemented my solution as a markdown extension because I wanted to continue to use the markdown template filter. I'm already using it with the pygments extension I discussed earlier:
{{ blogpost.body|markdown:"pygments" }}
I was imagining that I could implement my attachments extension and then just change the code like so:
{{ blogpost.body|markdown:"pygments,attachments" }}
That's nice and tidy, right? But how will the attachments extension know which blogpost it's operating on? Attachments have generic foreign keys. How will it even know it's operating on a blogpost? And what about specifying a template for rendering those attachments? Blast! We will have to complicate the solution.
It turns out that markdown.py's extension system supports the passing of config parameters. If I pass three variables: the content type id, the object id, and the template, then I'll be able to identify which blog post to use, and which template to use. This is how that would look in markdown's extension config param syntax:
{{ blogpost.body|markdown:"pygments,attachments(ct=5,id=3,template=blog/attach.html" }}
Problem the first: the content type and id values are hard-coded in there. I can't drop variables into a string that's being passed to a template filter. Problem the second: django splits this big string argument into a list of extensions to pass to markdown by splitting on ',', so my config params are interpreted as extension boundaries. There are other mysteries nestled within markdown.py's extension param handling system, but I'll talk about those in another post of I ever come out with a final draft for this dang thing.
So, I can only specify one parameter per extension if I'm passing it in through the template filter. I can't pass in the blogpost identifying data there anyway, so this works out: I'd just pass in the name of the template I want it to use for rendering the attachments. But that still leaves the problem of identifying the blog post to the attachments extension. Without that information, the extension will be looking up the specified filename in the set of all attachments in the system. The larger the site, the greater the risk of namespace collisions.
I think there are three solutions to this problem, and I don't like any of them all that much:
-
Implement a method on BlogPost that converts its body text from markdown to html, applying the appropriate markdown extensions, taking one optional parameter: the template to use for the attachments. If I wanted to do this, I'd have to figure out some way of invoking the method with a template argument from within a template. Here's the least awful hack I can think of for that:
{{ blogpost|render_body:"blog/attach.html" }}
That template filter could just invoke the method on the blogpost object, passing that variable. It works, but it's ugly, and it replaces a standard template filter (markdown) with a custom template filter that depends on a custom method.
-
Write a template tag for rendering the body of a blogpost. That would look like this:
{% render_body blogpost "blog/attach.html" %}
That's less of a hack than the first option, and I can keep all the markdown invocations inside the template tag, which spares the BlogPost model from having to deal with it. It's still a custom template tag replacing a standard template filter.
This wouldn't be so bad except that inside the template tag I'm still invoking the markdown by passing it a list of extension names. My attachments extension has to be live in the special markdown extension format of a file called mdx_attachments.py that lives in the root dir of some place in the python path. If I peel back the layers of markdown even further I could pass in my extension object directly without this mdx file, but it feels silly to put all this effort into disabling the handy extension loading functionality that I'm getting for free from markdown.py
-
The last option is to try and stay compatible with markdown and the markdown template filter. I could pass the object identifying information via a template tag provided by the attachments app. It would look like this:
{{ blogpost.body|annotate:blogpost|markdown:"pygments,attachments(template=blog/attach.html)" }}
This makes for verbose, less legible template code, but it keeps my interference in the markdown system to a minimum. The additional problem is how the annotation would work. Other than doing something crazy like subclassing
strand adding the variables there, I think the only way would be to add a line to the top of the text that looked something like this:[attach_object:5,3]
The arguments here are the content type id and the object id. With this, the attachment extension could identify the object first, before it began handling the
elements.I think I like this solution best. Is this crazy?


Why not turn autoescaping off and stick in an img tag?
I kind of don't understand. Not just your mission, here, but the whole fear of storing (X)HTML in the database. First of all, this is your blog, right? You can trust yourself to enter clean HTML, can't you? You're not going to try to hijack your own page with Javascript, I trust?
And even if it's not just content you're creating yourself, it seems to me much easier to restrict someone to a subset of HTML than to extend Markdown to do accommodate particular use cases. As you're showing here.
Bottom line: I say it's way preferable to make HTML more Markdown-like than to make Markdown more HTML-like.
— Hank Sims (January 03, 2009 at 1:39 a.m.)