tags:

views:

516

answers:

3

I use Django's render_to_response to return an XML document. This particular XML document is intended for a flash-based charting library. The library requires that the XML document start with a BOM (byte order marker). How can I make Django prepent the BOM to the response?

It works to insert the BOM into the template, but it's inconvenient because Emacs removes it every time I edit the file.

I have tried to rewrite render_to_response as follows, but it fails because the BOM is being UTF-8 encoded:

def render_to_response(*args, **kwargs):
    bom = kwargs.pop('bom', False)
    httpresponse_kwargs = {'mimetype': kwargs.pop('mimetype', None)}
    s = django.template.loader.render_to_string(*args, **kwargs)
    if bom:
        s = u'\xef\xbb\xbf' + s
    return HttpResponse(s, **httpresponse_kwargs)
A: 

The simplest thing might be to configure Emacs not to remove the BOM.

But render_to_response is not a complicated function. It's basically:

def render_to_response(*args, **kwargs):
    return HttpResponse(loader.render_to_string(*args, **kwargs))

You could easily call render_to_string and prepend the BOM.

Ned Batchelder
+2  A: 

You're not really talking about a BOM (Byte Order Mark), since UTF-8 doesn't have a BOM. From your example code, the library expects the text to have 3 garbage bytes prepended for some inexplicable reason.

Your code is almost correct, but you must prepend the bytes as bytes, not characters. Try this:

def render_to_response(*args, **kwargs):
    bom = kwargs.pop('bom', False)
    httpresponse_kwargs = {'mimetype': kwargs.pop('mimetype', None)}
    s = django.template.loader.render_to_string(*args, **kwargs)
    if bom:
        s = '\xef\xbb\xbf' + s.encode("utf-8")
    return HttpResponse(s, **httpresponse_kwargs)
John Millikin
I would imagine it *is* UTF-8 we're talking about. Some (typically Microsoft-derived) software likes to put BOMs in UTF-8 despite their being pointless and harmful. An XML reader that requires one, though, it broken and not really an XML reader at all.
bobince
There's no such thing as a BOM in UTF-8, because UTF-8 doesn't have a byte order. If some software depends on random garbage bytes being present in a file, that's something different.
John Millikin
@Vebjorn: I've updated the answer to reflect your edited question. Your code is almost correct already.
John Millikin
A: 

This solution, based on a previous version of John Millikin's answer, is more complex than the one I accepted, but I'm including it here for completeness. First, define a middleware class:

class AddBOMMiddleware(object):
    def process_response(self, request, response):
        import codecs
        if getattr(response, 'bom', False):
            response.content = codecs.BOM_UTF8 + response.content
        return response

Add its name to MIDDLEWARE_CLASSES in your settings. Then redefine render_to_response:

def render_to_response(*args, **kwargs):
    bom = kwargs.pop('bom', False)
    httpresponse_kwargs = {'mimetype': kwargs.pop('mimetype', None)}
    rendered = django.template.loader.render_to_string(*args, **kwargs)
    response = django.http.HttpResponse(rendered, **httpresponse_kwargs)
    if bom:
        response.bom = True
    return response

Now, you can do render_to_response("foo.xml", mimetype="text/xml", bom=True) in order to prepend the BOM to a particular response.

Vebjorn Ljosa