<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Django on Enrique Soria</title><link>https://blog.enriquesoria.com/tags/django/</link><description>Recent content in Django on Enrique Soria</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Tue, 18 Nov 2025 18:24:02 +0100</lastBuildDate><atom:link href="https://blog.enriquesoria.com/tags/django/index.xml" rel="self" type="application/rss+xml"/><item><title>How to display a JSON value in Django admin (when using MySQL)</title><link>https://blog.enriquesoria.com/display-json-value-django-admin-list/</link><pubDate>Tue, 18 Nov 2025 18:24:02 +0100</pubDate><guid>https://blog.enriquesoria.com/display-json-value-django-admin-list/</guid><description>&lt;blockquote>
&lt;p>tldr; here&amp;rsquo;s a small working example of this: &lt;a href="https://github.com/EnriqueSoria/DisplayJsonValueInAdmin">https://github.com/EnriqueSoria/DisplayJsonValueInAdmin&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>Let&amp;rsquo;s say that, for some reason, we have a json stored in a &lt;code>TextField&lt;/code> or a &lt;code>JSONField&lt;/code> (&lt;a href="https://docs.djangoproject.com/en/3.1/ref/models/fields/#jsonfield">available since Django 3.1&lt;/a>) that stores a payload.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Webhook&lt;/span>(models&lt;span style="color:#f92672">.&lt;/span>Model):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> payload &lt;span style="color:#f92672">=&lt;/span> models&lt;span style="color:#f92672">.&lt;/span>TextField() &lt;span style="color:#75715e"># works also with models.JSONField()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">...&lt;/span> &lt;span style="color:#75715e"># more fields&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="showing-the-timestamp-as-a-datetime-in-the-admin-list">Showing the &lt;code>timestamp&lt;/code> as a &lt;code>datetime&lt;/code> in the admin list&lt;/h2>
&lt;p>If we wanted to display the &lt;code>timestamp&lt;/code> in the admin list, we could use some of the methods present in &lt;a href="https://books.agiliq.com/projects/django-admin-cookbook/en/latest/calculated_fields.html">this guide&lt;/a>. For instance, we could create a method in the admin:&lt;/p></description></item><item><title>TIL: La caché de `functools.lru_cache` se mantiene entre tests</title><link>https://blog.enriquesoria.com/til-functools-lru-cache-cached-between-tests/</link><pubDate>Tue, 03 Dec 2024 01:01:00 +0100</pubDate><guid>https://blog.enriquesoria.com/til-functools-lru-cache-cached-between-tests/</guid><description>&lt;p>Asumamos que tenemos una función cacheada con &lt;code>functools.lru_cache&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># utils.py&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">from&lt;/span> functools &lt;span style="color:#f92672">import&lt;/span> lru_cache
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>SETTINGS &lt;span style="color:#f92672">=&lt;/span> {&lt;span style="color:#e6db74">&amp;#34;DATABASE_NAME&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;default&amp;#34;&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">@lru_cache&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">get_setting&lt;/span>(key):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> SETTINGS[key]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Y un par de tests que comprueban su comportamiento:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># test_utils.py&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">import&lt;/span> pytest
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">from&lt;/span> utils &lt;span style="color:#f92672">import&lt;/span> get_setting
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">test_get_database_name&lt;/span>():
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">global&lt;/span> SETTINGS
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">assert&lt;/span> get_setting(&lt;span style="color:#e6db74">&amp;#34;DATABASE_NAME&amp;#34;&lt;/span>) &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#e6db74">&amp;#34;default&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">test_get_modified_database_name_after_modifying_it&lt;/span>():
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">from&lt;/span> utils &lt;span style="color:#f92672">import&lt;/span> SETTINGS
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> SETTINGS[&lt;span style="color:#e6db74">&amp;#34;DATABASE_NAME&amp;#34;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;modified&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">assert&lt;/span> get_setting(&lt;span style="color:#e6db74">&amp;#34;DATABASE_NAME&amp;#34;&lt;/span>) &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#e6db74">&amp;#34;modified&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Si lo ejecutamos comprobaremos que el segundo test falla, ya que devuelve el valor que tenía antes de ser modificado el &lt;code>SETTINGS&lt;/code>. Esto es debido a que la caché &lt;code>lru_cache&lt;/code> no se limpia entre tests.&lt;/p></description></item><item><title>TIL: Django relations are not cached in model instance</title><link>https://blog.enriquesoria.com/til-django-model-related-are-not-cached/</link><pubDate>Tue, 19 Nov 2024 19:34:00 +0100</pubDate><guid>https://blog.enriquesoria.com/til-django-model-related-are-not-cached/</guid><description>&lt;h2 id="evaluating-relation-multiple-times-using-all-performs-n-queries">Evaluating relation multiple times using &lt;code>.all()&lt;/code> performs &lt;code>n&lt;/code> queries&lt;/h2>
&lt;p>Input:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>order &lt;span style="color:#f92672">=&lt;/span> Order&lt;span style="color:#f92672">.&lt;/span>objects&lt;span style="color:#f92672">.&lt;/span>first()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">with&lt;/span> query_debugger(&lt;span style="color:#e6db74">&amp;#34;without prefetch&amp;#34;&lt;/span>):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> list(order&lt;span style="color:#f92672">.&lt;/span>lines&lt;span style="color:#f92672">.&lt;/span>all())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> list(order&lt;span style="color:#f92672">.&lt;/span>lines&lt;span style="color:#f92672">.&lt;/span>all())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> list(order&lt;span style="color:#f92672">.&lt;/span>lines&lt;span style="color:#f92672">.&lt;/span>all())
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Output:&lt;/p>
&lt;pre tabindex="0">&lt;code>without prefetch: 3 queries
&lt;/code>&lt;/pre>&lt;h2 id="evaluating-a-related-queryset-stored-in-a-variable-only-performs-one-query">Evaluating a related queryset stored in a variable only performs one query&lt;/h2>
&lt;p>Input:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>order &lt;span style="color:#f92672">=&lt;/span> Order&lt;span style="color:#f92672">.&lt;/span>objects&lt;span style="color:#f92672">.&lt;/span>first()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">with&lt;/span> query_debugger(&lt;span style="color:#e6db74">&amp;#34;storing related queryset in a variable&amp;#34;&lt;/span>):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> lines &lt;span style="color:#f92672">=&lt;/span> order&lt;span style="color:#f92672">.&lt;/span>lines&lt;span style="color:#f92672">.&lt;/span>all()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> list(lines)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> list(lines)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> list(lines)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Output:&lt;/p>
&lt;pre tabindex="0">&lt;code>storing related queryset in a variable: 1 queries
&lt;/code>&lt;/pre>&lt;h2 id="evaluating-a-prefetched-related-queryset-multiple-times-using-all-doesnt-perform-any-extra-query">Evaluating a prefetched related queryset multiple times using &lt;code>.all()&lt;/code> doesn&amp;rsquo;t perform any extra query&lt;/h2>
&lt;p>Input:&lt;/p></description></item><item><title>The story of how `ChildSourceListSerializer` became a thing</title><link>https://blog.enriquesoria.com/fixing-child-serializer-source-in-listserializer/</link><pubDate>Mon, 03 Apr 2023 17:07:00 +0100</pubDate><guid>https://blog.enriquesoria.com/fixing-child-serializer-source-in-listserializer/</guid><description>&lt;h2 id="the-story">The story&lt;/h2>
&lt;p>Once upon a time, there was a Django developer named Sarah who was working on a new API using Django REST Framework. She had a model &lt;code>Movie&lt;/code> which had a many-to-many relationship with &lt;code>Genre&lt;/code>. Sarah wanted to create an endpoint that returned a list of movies with their associated genres as a list of strings.&lt;/p>
&lt;p>Initially, Sarah tried to use a standard &lt;code>ListSerializer&lt;/code> to serialize the genres, but she quickly realized that the source attribute of the child serializer was not taken into account when serializing a list of objects. This meant that she was unable to customize the serialization of the child objects based on their source attribute.&lt;/p></description></item><item><title>Una gran experiència amb l'open source</title><link>https://blog.enriquesoria.com/experiencia-django-command-palette/</link><pubDate>Sat, 26 Nov 2022 13:20:00 +0100</pubDate><guid>https://blog.enriquesoria.com/experiencia-django-command-palette/</guid><description>&lt;p>Lo que ha pasat hui ha sigut increïble.&lt;/p>
&lt;h2 id="0756-am---el-descobriment">07:56 AM - El descobriment&lt;/h2>
&lt;p>Cada dia quan em desperte perd un rato amb el mòbil, mire notificacions, xarxes socials, etc&amp;hellip; cosa que no es molt recomanable però ey, com diu Cactus: &amp;ldquo;&lt;a href="https://www.youtube.com/watch?v=GB-vFtTtiys">ni som perfectes, ni volem ser-ho&lt;/a>&amp;rdquo;.&lt;/p>
&lt;p>Mentre perdia el temps mirant mastodon (&lt;a href="https://mastodon.social/@esoria_dev">on em podeu seguir&lt;/a>) he trobat un toot recomanant una llibreria per a &lt;a href="https://blog.enriquesoria.com/tags/django/">Django&lt;/a>:&lt;/p>
&lt;p>&lt;a href="https://github.com/rajasimon/django-command-palette">&lt;img src="https://gh-card.dev/repos/rajasimon/django-command-palette.svg" alt="rajasimon/django-command-palette - GitHub">&lt;/a>&lt;/p>
&lt;p>Es una llibreria simple, però que afegix un buscador que, personalment, trobe molt a faltar a l&amp;rsquo;admin de django.&lt;/p></description></item><item><title>Create mantainable env.sample using django-environ</title><link>https://blog.enriquesoria.com/mantainable-env-sample/</link><pubDate>Tue, 20 Sep 2022 08:00:00 +0200</pubDate><guid>https://blog.enriquesoria.com/mantainable-env-sample/</guid><description>&lt;p>Keeping your env.sample is a hard task, that&amp;rsquo;s why I created a way to have it updated automatically:&lt;/p>
&lt;p>&lt;a href="https://github.com/EnriqueSoria/update-django-environ-sample">&lt;img src="https://gh-card.dev/repos/EnriqueSoria/update-django-environ-sample.svg" alt="EnriqueSoria/update-django-environ-sample - GitHub">&lt;/a>&lt;/p>
&lt;h2 id="how-to">How-to:&lt;/h2>
&lt;p>Put every env var with its configuration in a &lt;code>ENV_DEFAULTS&lt;/code> variable, in a separate python file named &lt;code>environment_defaults.py&lt;/code> (exactly this file name)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># environment_defaults.py&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ENV_DEFAULTS &lt;span style="color:#f92672">=&lt;/span> dict(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> VARIABLE_WITH_DEFAULT_VALUE&lt;span style="color:#f92672">=&lt;/span>(str, &lt;span style="color:#e6db74">&amp;#34;default value&amp;#34;&lt;/span>),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> VARIABLE_WITHOUT_DEFAULT_VALUE&lt;span style="color:#f92672">=&lt;/span>str,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Instantiate &lt;code>Env&lt;/code> with &lt;code>ENV_DEFAULTS&lt;/code> on your Django settings file&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># settings.py&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">from&lt;/span> environ &lt;span style="color:#f92672">import&lt;/span> Env
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">from&lt;/span> environment_defaults &lt;span style="color:#f92672">import&lt;/span> ENV_DEFAULTS
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>env &lt;span style="color:#f92672">=&lt;/span> Env(&lt;span style="color:#f92672">**&lt;/span>ENV_DEFAULTS)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And ensure you are accessing it using &lt;code>env(&amp;quot;VAR_NAME&amp;quot;)&lt;/code> instead of &lt;code>env.str(&amp;quot;VAR_NAME&amp;quot;)&lt;/code>&lt;/p></description></item></channel></rss>