# Supporting t-strings from Python 3.14

**URL:** https://forum.djangoproject.com/t/supporting-t-strings-from-python-3-14/40925
**Category:** Django Internals
**Created:** [May 14, 2025, 12:13pm UTC](https://forum.djangoproject.com/t/supporting-t-strings-from-python-3-14/40925 "2025-05-14T12:13:52Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![adamchainz](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/adamchainz/32/26_2.png) [@adamchainz](https://forum.djangoproject.com/u/adamchainz)
#### Post date: [May 14, 2025, 12:13pm UTC](https://forum.djangoproject.com/t/supporting-t-strings-from-python-3-14/40925/1 "2025-05-14T12:13:52Z")

</div>

[t-strings](https://docs.python.org/3.14/whatsnew/3.14.html#pep-750-template-strings) have been merged into Python 3.14 for its beta 1 release ([May 7](https://blog.python.org/2025/05/python-3140-beta-1-is-here.html)).

Since reading the PEP and its [associated example repo](https://github.com/davepeck/pep750-examples), I’ve been noodling about how Django could use t-strings. I am sure others are eager to think about this too. Here are some ideas, I wonder what everyone else thinks.

## 1. `format_html()` and `format_html_join()`

Extend [`format_html()`](https://docs.djangoproject.com/en/5.2/ref/utils/#django.utils.html.format_html) and [`format_html_join()`](https://docs.djangoproject.com/en/5.2/ref/utils/#django.utils.html.format_html_join) to support t-string templates:

```python
from django.utils.html import format_html

format_html(t"Hello <strong>{user.name}</strong>")

```

This would be a natural extension to these functions. The t-string example in the Python documentation shows a theoretical `html` function, but provides no implementation. I think it makes sense that we’d make Django’s existing tool support this new syntax.

The documentation does show an “advanced” use case, where a t-string detects a dict argument and expands that into HTML atttributes:

```python
attributes = {"src": "shrubbery.jpg", "alt": "looks nice"}
template = t"<img {attributes}>"
assert html(template) == '<img src="shrubbery.jpg" alt="looks nice" class="looks-nice">'

```

I’m not sure what others think, but I am cautious about playing with such features in Django, especially the pre-existing `format_html()`. We can leave the experimentation to other t-string-powered HTML packages. I think supporting the most basic use cases only is sufficient for `format_html()`: after all, if you want more complexity, you can reach for Django templates.

One note: Previously, we deprecated calling `format_html()` with one argument, because developers would often put an f-string there without correct escaping ([blog post](https://adamj.eu/tech/2023/06/15/format-html/)). Adding t-string to suport to `format_html()` would require us to allow a single-argument form but only if the given argument is a t-string `Template`.

## 2. Raw SQL

There are various ways to run raw SQL in Django:

1. [`RawSQL`](https://docs.djangoproject.com/en/stable/ref/models/expressions/#django.db.models.expressions.RawSQL)
2. [`Manager.raw()` (and `QuerySet.raw()`)](https://docs.djangoproject.com/en/stable/topics/db/sql/#performing-raw-queries)
3. [`connection.cursor()`](https://docs.djangoproject.com/en/stable/topics/db/sql/#executing-custom-sql-directly)

It would be neat if we could support t-strings in these cases too, like:

```auto
with connection.cursor() as cursor:
    cursor.execute(t"SELECT * FROM example_book WHERE id = {id}")

```

I think we could use a simple implementation where we transform templated variables to placeholders, and store variables in a tuple, making the above example equivalent to:

```auto
cursor.execute("SELECT * FROM example_book WHERE id = %s", (id,))

```

I can see one risk here, which is that if the DB-API ends up being extended to support t-strings with some different interface, we might not be able to support that.

* * *

Those are the ideas I’ve thought about. Any other suggestions? Maybe translations?

---

<div class="post-metadata">

### Author: ![carltongibson](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/carltongibson/32/267_2.png) [@carltongibson](https://forum.djangoproject.com/u/carltongibson)
#### Post date: [May 14, 2025, 12:31pm UTC](https://forum.djangoproject.com/t/supporting-t-strings-from-python-3-14/40925/2 "2025-05-14T12:31:07Z")

</div>

We should definitely play here!

The PEP 750 examples repo has [an implementation of that html() function](https://github.com/davepeck/pep750-examples/blob/b3659b2d478ee13edba229274d3acec48f64089d/pep/web.py#L277). I don’t know how robust it is, so we’d need to go with care.

The rest of that file looks awfully like the core of a template backend (if we [wanted to implement one](https://docs.djangoproject.com/en/5.1/howto/custom-template-backend/)) 🤔

Did you not grab the`django-t-strings` PyPI name yet? 😜

---

<div class="post-metadata">

### Author: ![pauleveritt](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/pauleveritt/32/28513_2.png) [@pauleveritt](https://forum.djangoproject.com/u/pauleveritt)
#### Post date: [May 14, 2025, 1:51pm UTC](https://forum.djangoproject.com/t/supporting-t-strings-from-python-3-14/40925/3 "2025-05-14T13:51:41Z")

</div>

Thanks a bunch Adam for bringing this up and Carlton for pointing me at this.

First, as catch-up: a LOT has been going on behind the scenes. Including the start of an HTML templating system. I _almost_ have the site ready, but I…don’t. So I’ll share the news here, first time ever: [tdom](https://github.com/t-strings/tdom).

- Written by Andreas from PyScript based on his LONG experience doing this exact thing, repeatedly, in JavaScript frontends
- Browser-first (specifically, MicroPython friendly)
- Tooling-friendly

But most of all: the start of a community. “Yay, another templating system.” My hope: we build a community of interoperability, tooling, and middleware. We define a standard `Node` interface for interchange. A standard component definition about passing props. Whatever.

On to Adam’s points. I want to start with shippable component systems and themes that can be consumed a la carte in Django and/or Jinja2. We don’t have enough high-quality design skill in Python. We should stop spreading that skill across N systems. I’d love for folks to sit in a Django template and pull in well-tested, maintained components in a natural way. Hopefully beyond the ` __html__ ` protocol.

I have a PoC for [Jinja integration](https://pauleveritt), where components written in a new system get parsed into AST entry points. The hope: people make high-quality, tooling-friendly components that Jinja folks can easily consume. Probably even more natural in Django, which is more friendly to new “tags”.

In fact, what if Django could help invent the new tag/component syntax? Ruff and all the Python tooling would appreciate a standard.

For SQL, Phil Jones sql-tstring that was interesting.

Then after that, there’s a LOT we could all do to bring modern web DX to all of Python. I hope to build a place where we can all discuss, find some agreements, and make a big jump, fast.

---

<div class="post-metadata">

### Author: ![wsvincent](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/wsvincent/32/52_2.png) [@wsvincent](https://forum.djangoproject.com/u/wsvincent)
#### Post date: [May 14, 2025, 3:23pm UTC](https://forum.djangoproject.com/t/supporting-t-strings-from-python-3-14/40925/4 "2025-05-14T15:23:18Z")

</div>

@pauleveritt also is on a TalkPython episode TODAY that talks more about this: [Episode #505 - t-strings in Python (PEP 750) | Talk Python To Me Podcast](https://talkpython.fm/episodes/show/505/t-strings-in-python-pep-750)

---

<div class="post-metadata">

### Author: ![ryanhiebert](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/ryanhiebert/32/64_2.png) [@ryanhiebert](https://forum.djangoproject.com/u/ryanhiebert)
#### Post date: [May 15, 2025, 2:12am UTC](https://forum.djangoproject.com/t/supporting-t-strings-from-python-3-14/40925/5 "2025-05-15T02:12:04Z")

</div>

This is great! The maintainer of psycopg is also doing [some experiments with t-strings](https://github.com/psycopg/psycopg/issues/339#issuecomment-2849195294) for the `psycopg.sql` query building module, that I think would likely be very in line with the SQL idea you mention.

---

<div class="post-metadata">

### Author: ![leandrodesouzadev](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/leandrodesouzadev/32/8981_2.png) [@leandrodesouzadev](https://forum.djangoproject.com/u/leandrodesouzadev)
#### Post date: [May 15, 2025, 8:20pm UTC](https://forum.djangoproject.com/t/supporting-t-strings-from-python-3-14/40925/6 "2025-05-15T20:20:57Z")

</div>

Anthony has also has a [great video on t-strings](https://www.youtube.com/watch?v=_QYAoNCK574).

---

<div class="post-metadata">

### Author: ![yhay81](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/yhay81/32/32951_2.png) [@yhay81](https://forum.djangoproject.com/u/yhay81)
#### Post date: [July 28, 2026, 12:49am UTC](https://forum.djangoproject.com/t/supporting-t-strings-from-python-3-14/40925/7 "2026-07-28T00:49:18Z")

</div>

Picking up the “Maybe translations?” thread-end from @adamchainz — I’ve been working on exactly this, and I think translations may be the most practically valuable t-string lane for Django, because it eliminates a real bug class: `.format()` applied to the _translated_ string (or forgotten entirely), placeholder typos that only surface in one locale at runtime, and positional placeholders that can’t be reordered.

What works today, with stock gettext machinery, via [gettext-tstrings](https://github.com/yhay81/gettext-tstrings) (on PyPI, MIT):

```python
name = user.get_short_name()
_(t"Hello {name}") # catalog msgid: "Hello {name}"
lazy_gettext(t"Save changes") # gettext_lazy equivalent, renders per active language

```

The full sentence goes to the PO catalog; translators can reorder or repeat `{name}`; attribute access (`{user.name}`), calls, and translation-side format specs are rejected by a written convention ([SPEC.md](https://github.com/yhay81/gettext-tstrings/blob/main/SPEC.md)) enforced identically at extraction and render time. A nice surprise: because these msgids get the standard `python-brace-format` flag, `msgfmt --check-format` and Weblate validate placeholder integrity in existing translation pipelines with no configuration.

Two honest Django-specific gaps, which is why I’m posting here rather than claiming this is Django-ready.

**Extraction** : `makemessages` wraps xgettext, which cannot parse t-strings. Today extraction goes through Babel (the library ships a `babel.extractors` entry point). A Django integration would need either a Babel-based extraction step alongside `makemessages`, or upstream xgettext support — the former exists now, the latter is a longer road.

**API shape** : Django’s `gettext`/`gettext_lazy` accept `str`. t-string support would want parallel entry points (a `Template` overload or new names), plural handling via `ngettext(t"...", t"...", n)`, and integration with `django.utils.translation`’s active-language machinery — the library’s contextvar binding maps onto that cleanly, but the wiring is real design work.

Django’s contributing guidelines ask for new APIs to be validated as third-party packages first — that’s what this is trying to be for t-string i18n. If anyone here wants to experiment with it in a Django project (or tear the msgid convention apart before it fossilizes), I’d value that feedback a lot. For context on the wider discussion: CPython declined stdlib gettext support (gh-137353, with core-dev guidance that this belongs on PyPI), and Babel-native support is under discussion in python-babel/babel issue 1206.
