Saturday, July 31, 2010

Django csrf_token Template Tag Fix

I was pretty annoyed when I realized that the required Django template tag {% csrf_token %} printed the following code.
<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='6bda3605af31dd8595d2a67d0dda827b' /></div>
As the developer, I should have complete control over what HTML is being sent to the browser. After some tinkering around, I came up with this method of stripping it down to only the token.
{% with csrf_token as csrf_token_clean %}{{ csrf_token_clean }}{% endwith %}
Here is an example of how I used it.
<input type="hidden" name="csrfmiddlewaretoken" value="{% with csrf_token as csrf_token_clean %}{{ csrf_token_clean }}{% endwith %}" />

I was expecting to use the |cut filter to strip off the generated HTML, but I didn't need to. Enjoy!

9 comments:

  1. So to get rid of a single, invisible div (~32 characters), you added a whole new templatetag (~62 characters)?

    Seems like a waste of time to me. It's not like the original tag destroys a layout or makes it impossible to control the build of a form.

    ReplyDelete
  2. I had a project that needed to be valid HTML code (not xHTML). This was the cleanest solution with minimal effort and doesn't require any changes to Django itself.

    ReplyDelete
  3. A much easier way is to add '{% load my_template_tag_library %}' to the top of your template. my_template_tag_library simply needs to implement a csrf_token tag that does what you want. This will override the built-in csrf_token tag.

    ReplyDelete
  4. But I did not want to overwrite anything, or create my own custom tags. I wanted a simple way to bypass this problem. My backend framework needs to stay on the backend.

    ReplyDelete
  5. Hard to see why django wraps the hidden input field in hidden div. That's just ugly code. Not the first time when django generates ugly html, though.

    ReplyDelete
  6. I was also mystified as to why they felt the need to nest it in a div. It's just weird. But, I couldn't think of a situation where it'd be a problem.

    ReplyDelete
  7. I'd suggest that the invisible div is for HTML validation as well — the HTML specification does not allow inline-level elements (like a, input, span etc.) as direct children of a form. Any block-level element would work, but div is an OK choice as it's the least semantically-charged option.

    That said, Django inappropriately forcing XHTML on developers is a mistake I didn't expect to see since leaving Drupal.

    ReplyDelete
  8. For the googlers...

    This whole trick with {% with %} is completely unnecessary. You have to understand that {% csrf_token %} the tag (which generates the html) is different from {{ csrf_token }} the template variable (which just contains the value). So all you really need to do is this:

    <input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}" />

    ReplyDelete
  9. I don't believe the {{ csrf_token }} variable existed when I wrote this article. Thanks for the update.

    ReplyDelete