Rethinking: How to create HTML? f-string like with conditional-escape


Since I use htmx, I changed the way I develop with Django.

I use format_html() to have everything in one place now.

This means I need to type foo=foo, bar=bar over and over again to get the variables
into format_html().

I would like to get something like Pythons f-string with support for conditional_escape().

First I created my idea here: GitHub - guettli/python-custom-strings: Python Custom Strings (pre PEP)

And now I found that PEP 501 already exists and would solve this.

AFAIK, to get support from IDEs and linters, we need to change Python. This can’t be handled at the library level.

Would you support PEP-501 if it would be available in Python?

Do you have an advice: What could I do as next step now?

1 Like

If the root issue is the perceived need to manually pass the local variables into the format_html call, you do have a couple options - the usefulness of either being really dependent upon how you’re using this in your views (or utility functions).

  1. Put the variables in a dict and pass the dict as kwargs to the function. (eg. If all these variables are keys in a dictionary named variables, then you could call format_html(**variables)).

  2. Depending upon just how much “control” you have over the string being formatted, it might be safe enough to call format_html(**locals()).

Either one of these can reduce the “parameter explosion” of large numbers of variables being interpolated or formatted in the function call.

Thank you Ken for trying to help.

I really like to avoid to type foo=foo, bar=bar again and again to get variable into a safestring.

AFAIK this can only be changed by changing Python.

I asked at the python-ideas list here: Mailman 3 Custom-Strings: Combine f-strings and condtional_escape() - Python-ideas -

No, that’s not true - in addition to the two methods I provided earlier, I’ve come up with at least two more. (Well, you might consider them variations on a theme rather than unique solutions.) If you’re doing this in a class, you could create a method that gathers local variables and then calls the format method, or you could write a wrapper function that takes the current class and builds the function call.

I want to type less, not more.


name = ...
format_html('<div>Hello {name}</div>', name=name)

How can I reduce the number of words to type?

But I want to use a name inside the curly braces, since there could be many curly braces.

My prefered solution looks roughly like this:

name = ...
magic_method('<div>Hello {name}</div>')

Implementing magic_method() via inspecting the local variables of the caller is easy.

But IDEs and linters think that “name” does not get used and act accordingly.

Do you see a better solution then PEP-501?


Like Ken suggested: format_html('<div>Hello {name}</div>', **locals()) – this is already as short as name=name and stays as short even when the number of arguments increases.

That question is one that will be answered once it is available. But even if not, there is nothing stopping you then from writing the code as you intend.

Curious, doesn’t that mean duplicating code/templates? Assume I have a list under /users and click on a user to edit (which should take me to /users/1/edit). I understand that with htmx you would make a fetch request, replace the content div and update the history. But if I manually go to /users/1/edit I’d like to see the full page. So I see two/three ways of doing that in a DRY way:

  • Use a template snippet and in the case of the full page request use {% include %} to get it and in the case of a htmx request render that directly
  • Use one template and {% extends template_variable %} to switch between solely between an “empty” base template and a template including the header/footer around the content
  • Render the full page either way (performance wise often not that much slower) and then use htmx to swap in only a matching css selector?

Since @apollo13 has answered this better than I would have been able to, I don’t have anything to add for the majority of your questions. However, there’s one comment you made that I would like to address:

<opinion> This doesn’t bother me in the least.
I consider linters and IDEs to be “support” tools, providing occasionally-useful information. But I don’t rely upon them to drive my code, nor do I let the information they provide affect how my code is designed or written.
If I’ve got a proven code design pattern that works for us in the context in which we’re using it, tools that don’t accept that pattern indicate a problem with that tool - not the code. <opinion>

An alternative approach is to use a library for generating HTML with python syntax. See hyperpython. I experimented with making my own such library, but don’t maintain it - see this example in its repo.

1 Like

My IDE uses a different color if a variable exists, but does not get used. Lightgray means “not used”

I like this feature.

It would confuse me a lot, if the color is lightgray and the variable gets used.

thank you for your link. I added it to my list how to create HTML with Python.

For me “HTML” means “<…> … </…>”. I am very used to the less-than and greater-than signs.