Opened 14 years ago

Closed 9 months ago

#14831 closed New feature (fixed)

Django Template Style Guide

Reported by: Simon Meers Owned by: Ryan Cheley
Component: Documentation Version: 1.2
Severity: Normal Keywords: template, style, format
Cc: Tobias Kunze, Ryan Cheley Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Baptiste Mispelon)

It would be nice to have a standard template style that should be followed. For example, I like to indent my Django templates by two additional spaces every time I enter a new HTML or template block. The Django core templates vary greatly in regards to style and indentation. Much of the time template readability is sacrificed in favour of attempting to preserve the "appearance" of the generated HTML code.

Regardless of what is decided, it would be great to have at least some guidelines for templates added to https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/#template-style

Change History (23)

comment:1 by Gabriel Hurley, 14 years ago

Triage Stage: UnreviewedAccepted

I'm in favor of standards in general. What will probably bring this issue to a consensus most quickly would be the following:

  1. An overview of the various styles found in the current templates,
  2. A draft patch based on whatever the most common/best practice seems to be,
  3. A thread on Django Developers to let folks debate the options.

A few points that should be specified in a style guide for templates:

  • Indentation: how many spaces?
  • Placement of extends and load tags (at the top seems pretty standard).
  • Whether HTML inside a block should always be on its own line or if short blocks should be kept on one line. See the admin base_site template for examples of both.
  • Whether all code inside a block should be indented. (commonly the answer seems to be "no")

There are probably others, but this is what springs to mind so far.

Just to toss my two cents into the ring (how's that for a mixed metaphor?), here's what I think:

Even though there's no guiding document like PEP 8 for HTML, I think applying as much of PEP 8 as makes sense is going to be the simplest answer. It's an established and understood document that Python developers are accustomed to. A couple of items that would tend to apply:

  • Where whitespace will not affect rendered output, break up nested tags onto their own lines to keep from running over 80 characters.
  • Separate blocks (or other discrete groups of tags or logical sections) by blank lines.
  • Imports (i.e. load and extends) should be at the top of the file, on separate lines.
  • Proper whitespace within HTML tags. (<span class="timestamp hidden"> not <span class = " timestamp hidden" >)

I think overall readability and proper indentation of each individual template file is more important than what the HTML output looks like. I wrap my base templates with the spaceless tag though, so it matters less to me.

Use 4 spaces for indentation. (I know 2 spaces is popular for HTML among many developers, but PEP 8 recommends 4 and changing to 2 for templates doesn't offer any substantive improvement while it decreases the visual separation).

comment:2 by ramen, 14 years ago

I would like to see such a standards guide as well, especially regarding indentation. In addition to whether all code inside a block should be indented, there seems to be a variety of approaches to indentation around other template tags such as conditionals and loops. My preference would be to indent inside of all template tags that span multiple lines.

comment:3 by anonymous, 14 years ago

Severity: Normal
Type: New feature

comment:4 by Simon Meers, 13 years ago

Easy pickings: unset
UI/UX: unset

I think much of Gabriel's suggestions here make good sense. Though:

  • I think {% load A B C %} is OK; loading in templates is simpler than importing in python since there are no relative imports, from x import y, etc.
  • I think 4-spaces is too much; in python, nesting can (and should) be avoided; in HTML, deep nesting is unavoidable. I'd therefore advocate 2-space indentation or tabs (the width of which can be configured by your editor)

I personally also indent template tags and HTML tags alike; e.g.

  <ul>
    {% for x in y %}
      <li>{{ x }}</li>
    {% endfor %}
  </ul>

(though this obviously doesn't produce terribly pretty HTML, if anyone cares. I prefer pretty templates.)

This is dangerous bikeshedding territory, but it seems sensible to have a standard within core code, and would also allow the production of django-template emacs modes, TextMate bundles, etc.

comment:5 by Simon Meers, 13 years ago

Actually you can use "from" syntax in the load tag, can't you; in those cases it certainly makes sense to keep them on separate lines. Though perhaps that isn't what Gabriel was advocating in the first place; on re-reading he may have been simply saying it shouldn't be on the same line as {% extends %} (which of course must be at the top).

comment:6 by Tobias Kunze, 6 years ago

Cc: Tobias Kunze added

By now, there is a place to add further template style guides (currently it only says to add one space between {{ and the variable name. So we'd need a consensus on what a good template style would mean: Indented for template readability or for meaningful HTML indentation? Indented by two spaces or four?

I'd be all for two spaces and indentation as proposed by Simon.

comment:7 by Ryan Cheley, 11 months ago

Cc: Ryan Cheley added

It looks like there hasn't been much movement on this ticket for a while, and the docs located here haven't made any changes to the Template Section. I'm wondering if this is actually something that is still needed or not?

If it is, I'm happy to try and gather consensus on what the docs should recommend.

If not, perhaps we just mark the ticket as closed?

comment:8 by Ryan Cheley, 11 months ago

Owner: changed from nobody to Ryan Cheley
Status: newassigned

comment:9 by Natalia Bidart, 11 months ago

Hello Ryan, thanks for going thru these old tickets to try to make a decision about them.

Given that the ticket is already accepted, I would suggest to push this ticket forward by pursuing the 3 tasks detailed in the first comment (with the only caveat that the Django Forum would be preferred instead of the Django Developers list), which I agree are the first steps before making any docs changes:

  1. An overview of the various styles found in the current templates,
  2. A draft patch based on whatever the most common/best practice seems to be,
  3. A thread on the Django Forum to let folks debate the options.

Looking forward to your forum post!

comment:10 by Ryan Cheley, 11 months ago

I ran the following bash command (which is returning some false positives, for example {al is finding an inline javascrpt alert) to get a sense of what is current in the django/django directory

find django/django -type f -name "*.html" -exec grep -E "(\{\{[^}]*\}\})|(\{%[^%]*%\})" {} + | grep -o -E "\{.." | sort | uniq -c

This gives the following usages

{{

Count Symbol
724 {{
2 {"|
2 {al
1 {if
1 {re
1 {vi
1 {x=
1 {{s

{%

Count Symbol
2121 {%
1 {%-

For the items that don't conform to {{ or {% I'll look more deeply at the occurrences and provide more context for them, but on initial review it seems that the preference / standard seems to be {{ or {% followed by a space

comment:11 by Ryan Cheley, 11 months ago

After reviewing the html files this is what I've found:

False positives from the script above

  • ./contrib/admindocs/templates/admin_doc/bookmarklets.html has inline javascript on line 22 which is the source of:
Count Symbol
2 {al
1 {if
1 {re
1 {vi
1 {x=
  • ./contrib/admindocs/templates/admin_doc/template_filter_index.html line 23 which is triggering the {"|. This seems to fall outside of the requirements for this ticket
  • ./contrib/admindocs/templates/admin_doc/template_tag_index.html line 23 which is triggering the {"|. This seems to fall outside of the requirements for this ticket
  • ./forms/jinja2/django/forms/widgets/multiwidget.html line has {%-`. Per the jinja2 docs this is in place for whitespace control

Files where the standard isn't being followed

  • ./views/templates/technical_500.html on line 157 has <td>{{server_time|date:"r"}}</td>

Based on these findings it seems that the docs that are currently written are being enforced.

Next steps:

  • [X] review extends and load tag placement
  • [ ] placement of HTML in block
  • [ ]indentation practices for code inside of block
Last edited 10 months ago by Ryan Cheley (previous) (diff)

comment:12 by Ryan Cheley, 10 months ago

Checking the {% extends} using this bash command

find . -name "*.html" ! -path "./venv/*" -exec grep -n "% extends" {} + | awk -F: '$2 > 1 {print "Filename: " $1 ", Line number: " $2 ", Matched string: " $3}'

returns no results. It looks like the {{% extends}} is always at the top line of the html files

Last edited 10 months ago by Ryan Cheley (previous) (diff)

comment:13 by Ryan Cheley, 10 months ago

Checking the {% loads using this bash command

find . -name "*.html" ! -path "./venv/*" -exec grep -En "\{% load" {} + | awk -F: '$2 > 2 {print "Filename: " $1 ", Line number: " $2 ", Matched string: " $3}'

returns the following results:

  • Filename: ./django/contrib/admin/templates/admin/auth/user/change_password.html, Line number: 3, Matched string: {% load admin_urls %}
  • Filename: ./django/contrib/admin/templates/admin/index.html, Line number: 25, Matched string: {% load log %}

The first item is actually OK upon inspection as there are 2 {% load ... } statements in the file

The second item ./django/contrib/admin/templates/admin/index.html has {% load log %} on line 25. This was first added in commit 52f5c9 so it's always been lower in the file.

Next step is to determine if moving the line up will break anything

comment:14 by Ryan Cheley, 10 months ago

I moved the {% load log %} from line 25 to line 3 and there weren't any NEW test failures. Once a standard is decided on this could potentially be moved to the top and it doesn't look like any negative impact would be introduced (from a testing perspective anyway).

Next step is to create a Django app locally and see what happens

in reply to:  14 comment:15 by Ryan Cheley, 10 months ago

Replying to Ryan Cheley:

I moved the {% load log %} from line 25 to line 3 and there weren't any NEW test failures. Once a standard is decided on this could potentially be moved to the top and it doesn't look like any negative impact would be introduced (from a testing perspective anyway).

Next step is to create a Django app locally and see what happens

App locally seems to be behaving as expected.

Also, in reading the docs I was reminded that a {% load ... %} can have more than one template tag added, so the two outliers here can be moved up into a single {% load ... } command instead of multiple lines

This means that

  • /django/contrib/admin/templates/admin/auth/user/change_password.html can have line 2 updated to {% load i18n static admin_urls %}
  • ./django/contrib/admin/templates/admin/index.html can have line 2 updated to be {% load i18n static log %}

comment:16 by Ryan Cheley, 10 months ago

Looking at the {% block ... %} I have this to locate the occurences

find . -name "*.html" ! -path "./venv/*" -exec grep -En "\{%\s*block\s+\w+\s*%\}" {} + | awk -F: '$2 > 2 {print "Filename: " $1 ", Line number: " $2 ", Matched string: " $3}'

This returns 365 files in 37 directories.

Further investigation will have to wait until next time though

comment:17 by Ryan Cheley, 10 months ago

I made a slight tweak to the script above

find . -name "*.html" ! -path "./venv/*" ! -path "*/build/*" -exec grep -En "\{%\s*block\s+\w+\s*%\}" {} + | awk -F: 'length($3) > 80 && $2 > 2 {print "\n# Filename: " $1 "\nLine number: " $2 "\nMatched string: \"" $3,"\""}' > results.md

There were 23 matches.

This will only get lines that are longer than 80 characters and then writes it to a file called results.md. Initial visual inspection of that file finds the following:

  1. the <link ...> tends to be on a single line, see for example /django/contrib/admin/templates/admin/base.html Line 6
  2. Simple logic for html element classes tends to be on a single line, see for example ./docs/_theme/djangodocs/layout.html Line 80
  3. Single elements can are on a single line, see for example ./django/contrib/admin/templates/admin/base.html Line 102

There are two files which may fall outside of the standard:

  • ./django/contrib/admin/templates/admin/auth/user/change_password.html Line 19
  • ./django/contrib/admin/templates/admin/change_form.html Line 36

In each of those cases there are block elements at the end of the line so these seem more like false positives from my script that actual issues

The next step is going to be to post to the Forum and the Discord server for next steps

comment:18 by Ryan Cheley, 9 months ago

Post to forum made here

Last edited 9 months ago by Ryan Cheley (previous) (diff)

comment:19 by Baptiste Mispelon, 9 months ago

Description: modified (diff)

comment:20 by Ryan Cheley, 9 months ago

Has patch: set

comment:21 by Ryan Cheley, 9 months ago

Patch needs improvement: set

comment:22 by Mariusz Felisiak, 9 months ago

Patch needs improvement: unset
Triage Stage: AcceptedReady for checkin

comment:23 by Mariusz Felisiak <felisiak.mariusz@…>, 9 months ago

Resolution: fixed
Status: assignedclosed

In f2c35249:

Fixed #14831 -- Extended template style guide in docs.

Note: See TracTickets for help on using tickets.
Back to Top