<?xml version="1.0" encoding="utf-8" ?>
<feed xmlns="http://www.w3.org/2005/Atom"
      xmlns:dc="http://purl.org/dc/elements/1.1/"
      xml:base="https://reinout.vanrees.org/" xml:lang="en">
  <link rel="self"
        href="https://reinout.vanrees.org/weblog/pythonfeed.xml" />
  <link href="https://reinout.vanrees.org/weblog/"
        rel="alternate" type="text/html" />

  <title type="html">Reinout van Rees' weblog</title>
  <subtitle>Python, grok, books, history, faith, etc.</subtitle>
  <updated>2026-04-17T14:19:00+01:00</updated>
  <id>urn:syndication:a55644db8591c020bd38852775819a9a</id>

  
    <entry>
      <title>Djangocon EU: lightning talks day 3</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/04/17/9-lightning-talks-day3.html" />
      <id>http://reinout.vanrees.org/weblog/2026/04/17/9-lightning-talks-day3.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-04-17T00:00:00+01:00</published>
      <updated>2026-04-17T14:19:00+01:00</updated>

      
        <category term="django" />
      
        <category term="djangocon" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/tags/djangocon.html">my summaries</a> of
the <a class="reference external" href="https://2026.djangocon.eu/">2026 Djangocon EU in Athens</a>).</p>
<div class="section" id="announcement-carlton-gibson">
<h1>Announcement - Carlton Gibson</h1>
<p>They've been working on improving the technical governance of Django. They'd like to get
feedback. There's a <a class="reference external" href="https://www.djangoproject.com/weblog/2026/apr/16/new-technical-governance-request-for-community-fee/">blog post about it</a>.</p>
<p>Oh, and look at the &quot;30% off PyCharm&quot; button on the django website, that raises quite a
lot of funds for Django. PyCharm's sponsoring is a very sizeable financial part of
Django, thanks!</p>
</div>
<div class="section" id="even-more-table-partitioning-with-django-postgres-and-uuids-tim-bell">
<h1>Even more table partitioning with Django, Postgres and UUIDs - Tim Bell</h1>
<p>(See his earlier talk on partitioning).</p>
<p>UUID is 128-bits, usually displayed as hex strings. It starts with the unix
timestamp, followed by several random fields (in version 7). In version 8, you have more
flexibility. You can customize it to put a specific value (an id of a related field in
their case) in the first field.</p>
<p>Partitioning per UUID (they used it as their ID) then effectively <em>also</em> partitions on
the related field.</p>
</div>
<div class="section" id="speeding-up-django-startup-times-with-lazy-imports-anze-pecar">
<h1>Speeding up Django startup times with lazy imports - Anze Pecar</h1>
<p>Imports in Python can be slow. Luckily, python has something build-in to check it, the
&quot;importtime&quot; flag:</p>
<pre class="literal-block">
python -X importtime manage.py check
</pre>
<p>He worked around the packages he found by importing the package inside the functions
where he used them. It worked, but it was ugly.</p>
<p>Look at things like <tt class="docutils literal">post_worker_init</tt> in gunicorn, you can use that to pre-load the
offending modules.</p>
<p>You can also wait for python 3.15. PEP810: explicit lazy imports!</p>
</div>
<div class="section" id="pyladies-seoul-rebooting-a-community-for-women-in-tech-scenes-hwayoung-cha">
<h1>PyLadies Seoul: rebooting a community for women in tech scenes - Hwayoung Cha</h1>
<p>At Pycon Korea 2023 there were only three woman in attendance. So: time to re-start
Pyladies Seoul! And with success. One of the new attendees is now a CTO of a company
(and also a PyLadies volunteer herself).</p>
<p>They'll also start a Django workshop soon.</p>
<p>Join your local PyLadies chapter!</p>
</div>
<div class="section" id="what-i-learned-during-learning-to-solve-rubic-cube-venelin-stoykov">
<h1>What I learned during learning to solve rubic cube - Venelin Stoykov</h1>
<p>He learned solving a Rubic cube in about two weeks.</p>
<p>We can learn new things more easily by association with things we already know. We need
to practice a lot. Repeat, repeat: that way we tell our brain that we need to remember
it.</p>
<p>&quot;Thinking slow and fast&quot; is a book he recommends.</p>
<p>AI is like the fast thinking. Fast is also a bit sloppy and often a bit wrong.</p>
<p>If we really want to understand something, it takes time and work.</p>
</div>
<div class="section" id="why-volunteering-and-contributing-to-communities-is-important-alex-gomez">
<h1>Why volunteering and contributing to communities is important - Alex Gómez</h1>
<p>Get involved! Volunteer! Do some work! Volunteers are necessary.</p>
<p>Volunteering is a lot of work, but it is worth it.</p>
</div>
<div class="section" id="djangofmt-a-django-template-formatter-written-in-rust-thibaut-decombe">
<h1>Djangofmt, a Django template formatter written in rust - Thibaut Decombe</h1>
<p>Djangofmt, a fast, html aware, django template formatter, written in Rust.</p>
<p><a class="reference external" href="https://github.com/UnknownPlatypus/djangofmt">https://github.com/UnknownPlatypus/djangofmt</a></p>
<p>You can run it as a pre-commit hook.</p>
<img alt="https://reinout.vanrees.org/images/2026/kat9.jpeg" src="https://reinout.vanrees.org/images/2026/kat9.jpeg" />
<p><em>Unrelated photo explanation: a cat I encountered in Athens in the morning near the
hotel.</em></p>
</div>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>Djangocon EU: supply chain attacks on Python projects - Mateusz Bełczowski</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/04/17/8-dependencies-supply-chain-attacks.html" />
      <id>http://reinout.vanrees.org/weblog/2026/04/17/8-dependencies-supply-chain-attacks.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-04-17T00:00:00+01:00</published>
      <updated>2026-04-17T13:48:00+01:00</updated>

      
        <category term="django" />
      
        <category term="djangocon" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/tags/djangocon.html">my summaries</a> of
the <a class="reference external" href="https://2026.djangocon.eu/">2026 Djangocon EU in Athens</a>).</p>
<p>Full title: what's in your dependencies? Supply chain attacks on Python projects.</p>
<p>How supply chain attacks work: attackers don't attack your code directly, they target
something you trust. A typical Django project has lots of dependencies. Direct
dependencies and &quot;transitive dependencies&quot;, dependencies of our dependencies. If you
depend on <tt class="docutils literal">requests</tt>, <tt class="docutils literal">requests</tt> itself will grab <tt class="docutils literal">certify</tt> and <tt class="docutils literal">urllib3</tt>.</p>
<p>Possible package attacks:</p>
<ul class="simple">
<li>Inject malicious code directly into the repo.</li>
<li>Create malicious package. Typosquatting (abusing typos), slopsquatting (abusing typos
made by LLMs). &quot;Brandjacking&quot;: quickly after <tt class="docutils literal">deepseek</tt> became popular, a
<tt class="docutils literal">deepseekai</tt> package was published that stole credentials.</li>
<li>Compromise existing package. Credential stealing, CI/CD exploits.</li>
</ul>
<p>What attackers typically do with access is to steal credentials. Environment variables,
cloud keys (<tt class="docutils literal">AWS_xyz</tt>), pypi tokens, ssh private keys, database URLs, saved passwords.</p>
<p>Example: num2words was hacked in July 2025. Phishing leading to maintainer credentials
theft. Fake login page at pypj.org instead of pypi.org. Then they uploaded faulty
releases with the captured credentials. Credentials weren't rotated, so a second attack
happened a few days later. This malware targeted <tt class="docutils literal">.pypirc</tt> files, leading to more
compromises.</p>
<p>How can we defend from this kind of attacks? Depends on the kind of attack. When
publishing via GitHub actions, use &quot;trusted publishing&quot;, in that case there are no
credentials to steal.</p>
<p>Another example: LiteLLM was compromised via trivy, a security scanner that itself was
compromised... It in turn collected environment variables, secrets and ssh keys, bundled
it all in a tarball and posted it to some legitimate-looking domain.</p>
<p>Some myths:</p>
<ul class="simple">
<li>&quot;Lockfiles protect us&quot;. No, they only prevent accidental upgrades, not when adding a
package for the first time.</li>
<li>&quot;Just don't install suspicious packages&quot;. Lots is installed via transitive
dependencies.</li>
<li>&quot;We run everything in Docker so we're safe&quot;. It limits the blast radius, but credentials
and environment variables are still at risk.</li>
<li>&quot;We can fully prevent attacks&quot;.</li>
</ul>
<p>Some tips:</p>
<ul class="simple">
<li>Use dependency cooldowns. <tt class="docutils literal">uv</tt> has &quot;exclude-newer&quot;, <tt class="docutils literal">pip</tt> has &quot;uploaded-prior-to&quot;.
Don't be the first to install a fresh release, as most malicious packages are
discovered within hours or days.</li>
<li>Pin versions and verify hashes.</li>
</ul>
<p>Pypi is getting better:</p>
<ul class="simple">
<li>Trusted publishing.</li>
<li>Project quarantine.</li>
<li>Attestations: cryptographic tools to verify the source.</li>
<li>Typosquatting protection.</li>
</ul>
<p>AI has risks:</p>
<ul class="simple">
<li>Slopsquatting. Hallucinated package names that get exploited.</li>
<li>Prompt injection via github issues.</li>
<li>Agents often &quot;just&quot; pip-install things directly.</li>
</ul>
<p>Note: AI can also be used to detect malware! A small project started after the LiteLLM
compromise managed to detect a dangerous different compromise almost the moment it was
published.  Nice!</p>
<img alt="https://reinout.vanrees.org/images/2026/kat8.jpeg" src="https://reinout.vanrees.org/images/2026/kat8.jpeg" />
<p><em>Unrelated photo explanation: a cat I encountered in Athens on an evening stroll in the
neighbourhood behind the hotel. It was a cat on the hunt: relevant to the topic of this
talk :-)</em></p>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>Djangocon EU: Django templates on the frontend? - Christophe Henry</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/04/17/7-django-templates-frontend.html" />
      <id>http://reinout.vanrees.org/weblog/2026/04/17/7-django-templates-frontend.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-04-17T00:00:00+01:00</published>
      <updated>2026-04-17T12:25:00+01:00</updated>

      
        <category term="django" />
      
        <category term="djangocon" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/tags/djangocon.html">my summaries</a> of
the <a class="reference external" href="https://2026.djangocon.eu/">2026 Djangocon EU in Athens</a>).</p>
<p>It all started with formsets: you generate a new form based on other forms. You can use
it to create pretty fancy forms. But your designer can get quite creative. And you might
have variable forms that have to react to user input.</p>
<p>A common solution is to use htmx, but that means server requests all the time. And some
users have really bad connections. Regular requests aren't handy in that scenario.</p>
<p>He looked at django-rusty-templates: Django's template engine implemented in Rust. It
had a template parser that he could re-use. With OXC (javascript oxidation compiler) he
converted that to javascript.</p>
<p>That way, he could offload much of the django form creation handling to the frontend,
including reacting to user input and showing alerts.</p>
<p>The work-in-progress project is called django-template-transpiler:
<a class="reference external" href="https://github.com/christophehenry/django-template-transpiler">https://github.com/christophehenry/django-template-transpiler</a> . Don't use it for
production.</p>
<img alt="https://reinout.vanrees.org/images/2026/kat7.jpeg" src="https://reinout.vanrees.org/images/2026/kat7.jpeg" />
<p><em>Unrelated photo explanation: a cat I encountered in Athens on an evening stroll in the
neighbourhood behind the hotel.</em></p>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>Djangocon EU: How Django is helping to build the biggest X-ray observatory to date - Loes Crama</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/04/17/6-x-ray.html" />
      <id>http://reinout.vanrees.org/weblog/2026/04/17/6-x-ray.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-04-17T00:00:00+01:00</published>
      <updated>2026-04-17T12:09:00+01:00</updated>

      
        <category term="django" />
      
        <category term="djangocon" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/tags/djangocon.html">my summaries</a> of
the <a class="reference external" href="https://2026.djangocon.eu/">2026 Djangocon EU in Athens</a>).</p>
<p>She works at <a class="reference external" href="https://www.cosine.eu/">Cosine</a>, they develop measurement systems and space instrumentation. They
work for the space industry (ESA, NASA, etc).</p>
<p>They're now working on &quot;high-energy optics&quot;, the NewAthena x-ray observatory, the biggest
one to date. NewAthena: NEW Advanced Telescope for High-ENergy Astrophysics. Planned
launch is in 2037 on Ariane 6.4.</p>
<p>Talking about rocket science is cool, but where's the software? Within the company,
software is an internal service, supporting scientists. Handling data in many ways:
visualization, analysis, processing, management. Django plays a big role in all of this.</p>
<p>When you build something with Django in a scientific context, you really need to
understand the data. Workflows must be flexible. R&amp;D and production often don't need to
be strictly separated. Multiple datastores for various purposes (like an extra MongoDB,
for instance) is often handy.</p>
<p>Their application consists of:</p>
<ul class="simple">
<li>SXRO (silicon x-ray optics) database.</li>
<li>A Mysql database.</li>
<li>Django.</li>
</ul>
<p>The goal is to track all the components that go into the observatory. Status and
quality. Configuration and geometry. Component relationships. Inspections. History of
all the components.</p>
<p>The default <strong>Django admin</strong> is their primary method of using the application. Often, it
is said that the admin is not <em>not</em> <strong>not</strong> intended for end users. But they're using it
anyway. It is an internal tool for technical people. Most of them have a PhD: they can
handle such an interface. They've been using it for years.</p>
<p>There are some third party packages:</p>
<ul class="simple">
<li><a class="reference external" href="https://django-simple-history.readthedocs.io/en/stable/">django-simple-history</a>:
easy history.</li>
<li><a class="reference external" href="https://pypi.org/project/djangoql/">djangoql</a>: advanced queries for the search bar.</li>
<li>django-admin-rangefilter, django-admin-list-filter-dropdown,
django-admin-numeric-filter: little tools to tweak the filters on the right hand side.</li>
</ul>
<p>There are some separate forms, mostly for actions performed in the lab or cleanroom. For
instance a form where you can use an ipad to indicate defects in one of the components
by just drawing on the picture of the component.</p>
<p>There's also a REST API. Other software and data tools can use it to integrate with
Django:</p>
<ul class="simple">
<li>Observability tools (prometheus/grafana).</li>
<li>JupyterHub.</li>
<li>Stacking robots.</li>
</ul>
<p>They use: djangorestframework, drf-spectacular (API docs), django-filter (filtering via GET parameters).</p>
<p>Django is their software backbone. A general-purpose framework that's well suited for a
scientific context.</p>
<img alt="https://reinout.vanrees.org/images/2026/kat6.jpeg" src="https://reinout.vanrees.org/images/2026/kat6.jpeg" />
<p><em>Unrelated photo explanation: a cat I encountered in Athens on an evening stroll in the
neighbourhood behind the hotel.</em></p>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>Djangocon EU: where did it all BEGIN;? Django's transaction management - Charlie Denton & Sam Searles-Bryant</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/04/17/5-where-did-it-begin.html" />
      <id>http://reinout.vanrees.org/weblog/2026/04/17/5-where-did-it-begin.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-04-17T00:00:00+01:00</published>
      <updated>2026-04-17T10:05:00+01:00</updated>

      
        <category term="django" />
      
        <category term="djangocon" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/tags/djangocon.html">my summaries</a> of
the <a class="reference external" href="https://2026.djangocon.eu/">2026 Djangocon EU in Athens</a>).</p>
<p>Transactions are an SQL concept. You &quot;begin&quot; a transaction, do a bunch of statements and
at the end &quot;commit&quot; it. Only then does it actually show up in the database for other connections:</p>
<pre class="literal-block">
BEGIN;
  INSERT ...;
  UPDATE ...;
  INSERT ...;
COMMIT;
</pre>
<p>If something goes wrong, you can abort it with &quot;rollback&quot;:</p>
<pre class="literal-block">
BEGIN;
  INSERT ...;
  UPDATE ...;
  INSERT ...;
ROLLBACK;
</pre>
<p>The SQL default is to run in &quot;autocommit&quot; mode, so everything always updates the
database immediately.</p>
<p>The history of Django's transaction management:</p>
<ul class="simple">
<li>&lt;0.95 emulated sql's autocommit by executing <tt class="docutils literal">COMMIT;</tt> after every save.</li>
<li>0.95 introduced <tt class="docutils literal">with <span class="pre">commit_on_success():</span></tt>, which would give you a context manager,
at the end everything would be committed unless an error occurred. It did have a
corner case: you couldn't really nest <tt class="docutils literal">commit_on_success</tt>.</li>
<li>1.6 introduced <tt class="docutils literal">with <span class="pre">atomic():</span></tt>, mostly like <tt class="docutils literal">commit_on_success</tt>, but it
introduced <tt class="docutils literal">SAVEPOINT</tt>, allowing you to rollback a nested <tt class="docutils literal">.atomic()</tt> to the
savepoint that's created when you enter a nested 'atomic' block.</li>
<li>3.2 introduced <tt class="docutils literal">with atomic(durable=True):</tt>. If you had a nested atomic, a
transaction you thought was committed might be rolled back by another 'atomic'
wrapping the code. When you specify <tt class="docutils literal">durable=True</tt>, calling 'atomic' fails with an
error, as it isn't legal in this case.</li>
</ul>
<p>In their own projects they sometimes wanted to have more explicit control. They
sometimes had a check like <tt class="docutils literal">if assert not connection.in_atomic_block</tt>, but that didn't
look nice. They split <tt class="docutils literal">atomic()</tt> into separate <tt class="docutils literal">transaction()</tt>, <tt class="docutils literal">savepoint()</tt> and
<tt class="docutils literal">durable()</tt> context managers. Nicely explicit.</p>
<p>The code is here: <a class="reference external" href="https://github.com/kraken-tech/django-subatomic">https://github.com/kraken-tech/django-subatomic</a></p>
<p>A big inspiration for them was a talk by Aymeric Augustin at the 2013 djangocon.eu.
<a class="reference external" href="https://reinout.vanrees.org/weblog/2013/05/17/transactions-for-web-developers.html">Here's my summary</a>
of that talk.</p>
<img alt="https://reinout.vanrees.org/images/2026/kat5.jpeg" src="https://reinout.vanrees.org/images/2026/kat5.jpeg" />
<p><em>Unrelated photo explanation: a cat I encountered in Athens on an evening stroll in the
neighbourhood behind the hotel.</em></p>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>Djangocon EU: improving runserver with django-prodserver - Andrew Miller</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/04/17/4-improving-most-used-api.html" />
      <id>http://reinout.vanrees.org/weblog/2026/04/17/4-improving-most-used-api.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-04-17T00:00:00+01:00</published>
      <updated>2026-04-17T09:33:00+01:00</updated>

      
        <category term="django" />
      
        <category term="djangocon" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/tags/djangocon.html">my summaries</a> of
the <a class="reference external" href="https://2026.djangocon.eu/">2026 Djangocon EU in Athens</a>).</p>
<p>Original title: improving one of Django's most used APIs — and it's not the one you're
thinking of. (I'm using a more descriptive title for my blog entry.)</p>
<p>APIs are everywhere. Django rest framework, but also the models layer. And: <tt class="docutils literal">manage.py
runserver</tt>, he considers that an API. Everybody runs it. So: can we improve it?</p>
<p>&quot;Runserver&quot; doesn't sound the same as &quot;devserver&quot; or &quot;rundevserver&quot;. It doesn't
advertise that it is only intended for development. A name change could help there.
But... Django really likes to be stable. It is probably too entrenched to change.</p>
<p>Production is missing a cohesive API. You normally run something like <tt class="docutils literal">gunicorn
myapp.wsgi 0.0.0.0:8000 <span class="pre">--workers</span> 4</tt>...</p>
<p>He tried to get improvements in. Since 5.2, there's a warning when you start
<tt class="docutils literal">runserver</tt>: &quot;it is only intended for development&quot;. But you might miss it when
you get lots of log messages. Other people complained about the extra two lines.</p>
<p>He started <a class="reference external" href="https://django-prodserver.readthedocs.io/en/latest/">django-prodserver</a>.
<tt class="docutils literal">pip install <span class="pre">django-prodserver[gunicorn]</span></tt>. You can then run <tt class="docutils literal">manage.py prodserver</tt>
(and <tt class="docutils literal">manage.py devserver</tt>, he snuck that in).</p>
<p>You have to add a bit of configuration to your settings file:</p>
<pre class="literal-block">
PRODUCTION_PROCESSES = {
    &quot;web&quot;: {
        &quot;BACKEND&quot;: &quot;django_prodserver.backends.gunicorn.GunicornServer&quot;,
        &quot;ARGS&quot;: {&quot;bind&quot;: &quot;0.0.0.0:8000&quot;,},  # add number of workers and so.
    },
}
</pre>
<p>Run it with <tt class="docutils literal">manage.py prodserver web</tt>. You can add more processes, for instance for
a background worker process.</p>
<p>It is the first version. He wants feedback, especially on the naming. <tt class="docutils literal">manage.py
prodserver web</tt> or just <tt class="docutils literal">manage.py web</tt>? And <tt class="docutils literal">manage.py worker</tt>? <tt class="docutils literal">manage.py serve
<span class="pre">--prod</span></tt>?</p>
<p>Django isn't just a framework, it is a set of APIs. We can prototype new APIs in
packages.</p>
<img alt="https://reinout.vanrees.org/images/2026/kat4.jpeg" src="https://reinout.vanrees.org/images/2026/kat4.jpeg" />
<p><em>Unrelated photo explanation: a cat I encountered in Athens on an evening stroll in the
neighbourhood behind the hotel.</em></p>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>Djangocon EU: auto-prefetching with model field fetch modes in Django 6.1 - Jacob Walls</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/04/17/3-auto-prefetching.html" />
      <id>http://reinout.vanrees.org/weblog/2026/04/17/3-auto-prefetching.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-04-17T00:00:00+01:00</published>
      <updated>2026-04-17T08:29:00+01:00</updated>

      
        <category term="django" />
      
        <category term="djangocon" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/tags/djangocon.html">my summaries</a> of
the <a class="reference external" href="https://2026.djangocon.eu/">2026 Djangocon EU in Athens</a>).</p>
<p>There's an example to experiment with here: <a class="reference external" href="https://dryorm.xterm.info/fetch-modes-simple">https://dryorm.xterm.info/fetch-modes-simple</a></p>
<p>Timeline: it will be included in Django 6.1 in August.</p>
<p>The reason is the 1+n problem:</p>
<pre class="literal-block">
books = Book.objects.all()
for book in books:
    print(book.author.name)
    # This does a fresh query for author every time.
</pre>
<p>You can solve it with <tt class="docutils literal">select_related(relation_names)</tt> or
<tt class="docutils literal">prefetch_related(relation_names)</tt>. The first does an inner join. The second does two
queries.</p>
<p>But: you might miss a relation. You might specify too many relations, getting data you
don't need. Or you might not know about the relation as the code is in a totally
different part of the code.</p>
<p><strong>Fetch mode</strong> is intended to solve it. You can append <tt class="docutils literal">.fetch_mode(models.FETCH_xyz)</tt>
to your query:</p>
<ul class="simple">
<li><tt class="docutils literal">models.FETCH_ONE</tt>: the current behaviour, which will be the default.</li>
<li><tt class="docutils literal">models.FETCH_PEERS</tt>: Fetch a deferred field for all instances that came from the
same queryset. More or less <tt class="docutils literal">prefetch_related</tt> in an automatic, lazy manner.</li>
<li><tt class="docutils literal">models.FETCH_RAISE</tt>: useful for development, it will raise <tt class="docutils literal">FieldFetchBlocked</tt>.
And it will thus tell you that you'll have a performance problem and that you might
need FETCH_PEERS</li>
</ul>
<p>This is what happens:</p>
<pre class="literal-block">
books = Book.objects.all().fetch_mode(models.FETCH_PEERS)
for book in books:
    # We're iterating over the query, so the query executes and grabs all books.
    print(book.author.name)
    # We accessed a relation, so at this point the prefetch_related-like
    # mechanism ist fired off and all authors linked to by the books are
    # grabbed in one single query.
</pre>
<p>You can write your own fetch modes, for instance if you only want a warning instead of
raising an error.</p>
<img alt="https://reinout.vanrees.org/images/2026/kat3.jpeg" src="https://reinout.vanrees.org/images/2026/kat3.jpeg" />
<p><em>Unrelated photo explanation: a cat I encountered in Athens on an evening stroll in the
neighbourhood behind the hotel.</em></p>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>Djangocon EU: zero-migration encryption - Vjeran Grozdanic</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/04/17/2-zero-migration-encryption.html" />
      <id>http://reinout.vanrees.org/weblog/2026/04/17/2-zero-migration-encryption.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-04-17T00:00:00+01:00</published>
      <updated>2026-04-17T09:05:00+01:00</updated>

      
        <category term="django" />
      
        <category term="djangocon" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/tags/djangocon.html">my summaries</a> of
the <a class="reference external" href="https://2026.djangocon.eu/">2026 Djangocon EU in Athens</a>).</p>
<p>Full title: zero-migration encryption: building drop-in encrypted field in Django.</p>
<p>He works at Sentry. Huge site with a Django backend and thousands requests per second.</p>
<p>He had to add a new table to store 3rd party API credentials. Oh: should this be
encrypted? Yes. But: each team has its own way to encrypt data. And there were at least
10 encryption keys here and there (as environment variables). And tens of places where
encryption/decryption happens.</p>
<p>So: better to build a generic solution. Or use an existing generic solution. And yes,
there are multiple libraries. <tt class="docutils literal">EncryptedCharField</tt> looked nice. But the problem was
all the existing data in the various places. Sentry is not a site that you can shut down
for a while, so you have to do it with zero downtime. This means you can never change an
existing column type.</p>
<p>A solution could be to add a new encrypted field next to the existing one. Then fill it
and backfill it and make sure no new data is written to the old field and <em>then</em> you can
remove the old field. But that's quite a job with all the different locations that had
to be changed.</p>
<p>A <tt class="docutils literal">Field</tt> class in Django has <tt class="docutils literal">get_prep_value()</tt> and <tt class="docutils literal">from_db_value()</tt>. Those are
called before storing data in the database and after grabbing it from the database. You
could create a new CharField-like field and start to encrypt values in
<tt class="docutils literal">get_prep_value</tt> and decrypt the other way.</p>
<p>You'd have to be able to recognise the old un-encrypted values. A solution: prefix
encrypted values with <tt class="docutils literal">enc:</tt>. Also key rotation can be handled this way, by including
that in the prefix (<tt class="docutils literal">enc:key2:</tt>).</p>
<p>But there's also a bjson field. They solved that by encrypting the json and writing a
json to the database with the encrypted json in a field and also the encryption key
info.</p>
<p>The code is <a class="reference external" href="https://github.com/getsentry/sentry/tree/41a445555744b525c0c3d6f0f7c8a68412a04b62/src/sentry/db/models/fields/encryption">in the sentry repo</a> .</p>
<img alt="https://reinout.vanrees.org/images/2026/kat2.jpeg" src="https://reinout.vanrees.org/images/2026/kat2.jpeg" />
<p><em>Unrelated photo explanation: a cat I encountered in Athens on an evening stroll in the
neighbourhood behind the hotel.</em></p>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>Djangocon EU: body of knowledge - Daniele Procida</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/04/17/1-body-of-knowledge.html" />
      <id>http://reinout.vanrees.org/weblog/2026/04/17/1-body-of-knowledge.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-04-17T00:00:00+01:00</published>
      <updated>2026-04-17T07:36:00+01:00</updated>

      
        <category term="django" />
      
        <category term="djangocon" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/tags/djangocon.html">my summaries</a> of
the <a class="reference external" href="https://2026.djangocon.eu/">2026 Djangocon EU in Athens</a>).</p>
<p>Athens! The thinking industry started here. Athens is often the origin if you follow
ideas to the source.</p>
<p>Here also Socrates was found guilty (280-221) for &quot;corrupting the youth&quot; on trumped-up
charges. Though... he made it is job to be a complete nuisance: exposing everyone's
hypocrisy and asking difficult questions. After the 280-221 vote he got to give a speech
in reaction. After that, the vote on the actual punishment was 360-141 in favour of the
death penalty. The speech must have been particularly irritating.</p>
<p>On to a different subject. He watched the recent launch of the NASA rocket that went to
the moon. A marvel of technology. That was measured using body parts, being 322 feet
tall. And the distance to the moon in miles. Why not the scientific meter and kilometer?</p>
<p>Plato already mentioned it. &quot;Now take the acquisition of knowledge; is the body a
hindrance or not, if one takes it into partnership to share an investigation&quot;. And &quot;when
the soul tries to investigate anything with the help of the body, it is obviously led
astray&quot;.</p>
<p>0.098 km = 98 m = 98000 mm, a child can understand it. Pure rationality. But ask a
Metric Martyr in the UK how many feet are in a mile and most of them won't know.</p>
<p>The world seems to be divided in two camps:</p>
<ul class="simple">
<li>Thinking, rationality, abstraction, unboundedness.</li>
<li>Bodies, materiality, tangibility, being rooted.</li>
</ul>
<p>Wouldn't Plato have loved a computer? Pure rationality, following its logic programming
without fail?</p>
<p>What about those body-part-units? They're not that weird actually. They're rational
Roman measurements:</p>
<ul class="simple">
<li>A mile is 1000 Roman paces.</li>
<li>1 passus = 5 pedes (feet).</li>
<li>1/12 pes (feet) = 1 uncia (thus: inch).</li>
</ul>
<p>(Note: according to the Greek, Romans are only good for stealing Greek ideas, building
roads and killing people.)</p>
<p>On to another aspect. <strong>Why is Django's documentation so good?</strong> Well, it has been
prioritized from the start. It is complete, accurate, consistent, rational and
well-structured: all Platonic values.</p>
<p>But Daniele also thinks the documentation is so good because <strong>it fits the human body</strong>.</p>
<p>The size has to be right. The limitations of our intelligence are the limits of our
embodied intelligence. We can only grasp so much, mentally. A list can be too long. A
page can be too long. If information is cut in too-small parts, you also can get into
problems as you have to context-switch between pages too much. We tire mentally also
because we tire physically.</p>
<p>The same applies to our body. Our hands and fingers can grasp objects. But it has to be
of a certain size. Too big and we can't grasp it. Too small and our fingers can't pick it
up.</p>
<p>We experience documentation in time and space. We move with it. How long have you been
reading the Django documentation? &quot;Where are you in the text?&quot; We orient ourselves in
text as if in a space or a building. We rely on the <strong>humanised rationality of
structure</strong>. Sometimes you're in a building and it is clear where you have to go and in
other buildings you feel lost.</p>
<p>Django's documentation is so good because of the <strong>quality of experience</strong> that it gives
you. It is almost an embodied being that you can experience in space and time. Does it
fit you? Do you notice it? The embodied nature of the work and intelligence that the Django
community poured into the documentation?</p>
<p>Early Macintosh manuals had to explain new concepts and really tried to explain them in
a human way. Scrolling being explained with help of an old book scroll, for instance. A
floppy disk for storage as a floor plan of a building with a corridor and rooms.</p>
<p><a class="reference external" href="https://en.wikipedia.org/wiki/Aldine_Press">Aldine Press</a> (started 1494 in Venice)
had a vision to print the old classics in a more accessible way. Books in the middle
ages used to be big. And sometimes chained to the desk. Not really accessible. By
printing them in smaller, lighter, more accessible formats, he wanted to make our &quot;body
of knowledge&quot; more fitting to the human body.</p>
<p>You can see the bodily aspects of knowledge in our language:</p>
<ul class="simple">
<li>Seizing/taking: grasp, comprehend, apprehend, perceive.</li>
<li>Measuring: ponder, weigh up, fathom.</li>
<li>Body movement: jumping to conclusions, intuitive leap, stumble/trip</li>
<li>Spatiality: understand, position</li>
</ul>
<p>Mental space. When he asked a question of Russell Keith-Magee at a Django sprint, Russell
would close his eyes and turn his eye inwards for a while. He would look at the Django
codebase in his head and navigate it. Just like you yourself would navigate a city?</p>
<p>Being a programmer isn't so different from being a human with a body in time and space.
Look at questions you might have as a beginning programmer:</p>
<ul class="simple">
<li>Which file or directory or window <strong>to be in</strong>.</li>
<li><strong>Where</strong> to expect the output.</li>
<li><strong>When</strong> to expect it.</li>
<li><strong>Where</strong> to enter a command.</li>
<li><strong>When</strong> to do something.</li>
<li>In what <strong>order</strong> to do things.</li>
</ul>
<p>And then look at an experienced programmer. They seem to know where they are. They
know their way around. They can move smoothly.</p>
<p>Closing comment: there are some uncanny features in software nowadays. As a human, we
are used to having limits. But nowadays we have infinite scrolling, doomscrolling. And
edgeless, endless, virtual cloud resources. And LLM indeterminism. Those are not
inherintly bad, but it is different from what we're used to. Is this still <strong>computing
fit for the embodied mind</strong>?</p>
<img alt="https://reinout.vanrees.org/images/2026/kat1.jpeg" src="https://reinout.vanrees.org/images/2026/kat1.jpeg" />
<p><em>Unrelated photo explanation: a cat I encountered in Athens on an evening stroll in the
neighbourhood behind the hotel.</em></p>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>Djangocon EU: lightning talks (day 2)</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/04/16/9-lightning-talks-day2.html" />
      <id>http://reinout.vanrees.org/weblog/2026/04/16/9-lightning-talks-day2.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-04-16T00:00:00+01:00</published>
      <updated>2026-04-16T14:32:00+01:00</updated>

      
        <category term="django" />
      
        <category term="djangocon" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/tags/djangocon.html">my summaries</a> of
the <a class="reference external" href="https://2026.djangocon.eu/">2026 Djangocon EU in Athens</a>).</p>
<div class="section" id="developing-django-s-community-andy-miller">
<h1>Developing Django's community - Andy Miller</h1>
<p>Andy likes these conferences. But getting here and attending costs at least €1000. So
conferences are limited to those that can afford it.</p>
<p>The new <strong>online community working group</strong> that wants to improve the online possibilities
of gathering, as those are available to everyone.</p>
<p>Perhaps new virtual social events. Better feeds of what's happening in the community.
So: improve the community for everyone.</p>
<p>More info: <a class="reference external" href="https://github.com/django/online-community-working-group">https://github.com/django/online-community-working-group</a></p>
</div>
<div class="section" id="to-jwt-or-not-to-jwt-benedikt">
<h1>To JWT or not to JWT - Benedikt</h1>
<p><a class="reference external" href="https://www.jwt.io/">JSON web tokens</a> are not a one-size-fits-all solution.</p>
<p>JWT is a bas64 encoded string with three parts: header, payload, signature. Marketed as
stateless, but revocation always adds state.</p>
<p>Why would you want to use it? Well, third-party identity providers often give you one.
And: you can save database queries by embedding info in the token. And you can use it
for offline mode in mobile or desktop apps. But there are drawbacks.</p>
<p>Some reasons for using something else: JWTs are immutable, data remains valid until
expiration even when the data changes server-side. Stateless revocation is impossible.
Logout-from-all-devices requires tracking state, defeating the process.</p>
<ul class="simple">
<li>JWT if your provider uses them.</li>
<li>Regular Session/cookie auth for web apps is often better.</li>
<li>Opaque tokens for mobile/desktop.</li>
</ul>
</div>
<div class="section" id="django-on-the-med-django-italia-paolo-melchiorre">
<h1>Django on the Med / Django Italia - Paolo Melchiorre</h1>
<p>&quot;Django on the Med&quot; is a sprint. Not a sprint after a conference, but just a sprint.
After a conference you often want to get home or you're tired, so what we get done at a
conference sprint is often a bit limited.</p>
<p>What they got done in September at &quot;Django on the Med&quot; is amazing. This year it is in
Pescara, Italy. 23-25 September.</p>
<p>Somewhat related: 27 May there'll be a free &quot;Django off the Med&quot; online workshop at the
PyCon Italia conference.</p>
</div>
<div class="section" id="two-ways-i-used-generatedfield-during-a-rewrite-anthony-ricaud">
<h1>Two ways I used GeneratedField during a rewrite - Anthony Ricaud</h1>
<p>GeneratedField: google for Paolo, he's done lots of talks on it (<a class="reference external" href="https://djangotv.com/videos/djangocon-us/2025/djangos-generatedfield-by-example-with-paolo-melchiorre/">for instance this one</a>).</p>
<p>He used GeneratedField in a migration scenario:</p>
<pre class="literal-block">
archived = models.GeneratedField(
    output_field=models.BooleanField,
    db_persist=True,
    expression=(models.Q(soft_delete=True | models.Q(....)),
)
</pre>
<p>The second scenario involved generating a unique &quot;city id&quot; based on two other fields.</p>
</div>
<div class="section" id="django-mediastorage-alissa-gerhard">
<h1>django-mediastorage - Alissa Gerhard</h1>
<p>A media file is data that is stored as a file and accessible to users. FileFields store
files attached to models.</p>
<p>Local filesystem is the default and sufficient for most projects. You can use
<tt class="docutils literal"><span class="pre">X-Accell-Redirect</span></tt> to get the proxy (like nginx) to actually serve the file, instead
of Django. But every proxy has its own solution.</p>
<p><a class="reference external" href="https://pypi.org/project/django-mediastorage/">django-mediastorage</a> can handle it
for you for several different proxies.</p>
<p>There's a new FileField subclass, ProtectedFileField, to handle authentication
requirements.</p>
<p>There's also integration for django restframework.</p>
<p>There's still a lot to do, but they're using it in production themselves.</p>
</div>
<div class="section" id="django-vps-deployments-made-simple-jan-raasch">
<h1>Django VPS deployments made simple - Jan Raasch</h1>
<p>Let's talk about Django's deployment story.</p>
<p>He demoed deploying a simple Django app to a small virtual server. With <tt class="docutils literal">python
manage.py deploy <span class="pre">--serveral-options</span></tt>.</p>
<p>He used django-simple-deploy and a custom plugin for django-simple-deploy that used
'kamal' to do the actual deploying.</p>
<img alt="https://reinout.vanrees.org/images/2026/moezel9.jpeg" src="https://reinout.vanrees.org/images/2026/moezel9.jpeg" />
<p><em>Unrelated photo explanation: a trip in November to the Mosel+Eifel region in Germany.
Some restored remnants of the Virneburg castle.</em></p>
</div>
</div>

      ]]>
      </content>

    </entry>
  

</feed>