<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Valentino Gagliardi Blog</title>
  <subtitle>Valentino&#39;s blog on JavaScript and Python.</subtitle>
  <link href="https://www.valentinog.com/feed.xml" rel="self" />
  <link href="https://www.valentinog.com/blog/" />
  <updated>2025-03-15T11:36:39Z</updated>
  <id>https://www.valentinog.com/blog/</id>
  <author>
    <name>Valentino Gagliardi</name>
  </author>
  <entry>
    <title>Building accessible web forms in Django</title>
    <link href="https://www.valentinog.com/blog/django-accessible-web-forms/" />
    <updated>2025-03-15T11:36:39Z</updated>
    <id>https://www.valentinog.com/blog/django-accessible-web-forms/</id>
    <content type="html">&lt;p&gt;A step by step reference to building accessible web forms in Django.&lt;/p&gt;
&lt;p&gt;The complete source code can be found on &lt;a href=&quot;https://github.com/valentinogagliardi/django-accessible-forms&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;introduction&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-accessible-web-forms/#introduction&quot;&gt;Introduction&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Accessibility in Django is taken very seriously. However, as with almost everything, there is no size that fits all, and there is only so much Django can offer by default without resulting too opinionated.&lt;/p&gt;
&lt;p&gt;Recent version of Django are already improving the accessibility compartment.&lt;/p&gt;
&lt;p&gt;Starting from Django 5.0 for example, &lt;a href=&quot;https://docs.djangoproject.com/en/5.1/releases/5.0/#forms&quot;&gt;form fields&lt;/a&gt; receive the &lt;code&gt;aria-describedby&lt;/code&gt; attribute pointing to their help text if the latter is present. Also, invalid fields expose &lt;code&gt;aria-invalid=&amp;quot;true&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Starting from Django 5.1, &lt;a href=&quot;https://docs.djangoproject.com/en/5.1/releases/5.1/#forms&quot;&gt;fieldsets&lt;/a&gt; receive &lt;code&gt;aria-describedby&lt;/code&gt; as well.&lt;/p&gt;
&lt;p&gt;When dealing with web forms, we need to apply techniques to augment accessibility if needed, and be also very careful not to break existing capabilities.&lt;/p&gt;
&lt;p&gt;This guide offers a collection of techniques for building accessible web forms in Django, by improving the already good defaults.&lt;/p&gt;
&lt;h2 id=&quot;disclaimer&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-accessible-web-forms/#disclaimer&quot;&gt;Disclaimer&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Keep in mind that &lt;strong&gt;this reference is non-exhaustive&lt;/strong&gt; and that the changes proposed in this post might not suit your project! &lt;strong&gt;There is no &amp;quot;one size fits all&amp;quot; in web accessibility: every project has its requirements and constraints.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;progressive-enhancement&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-accessible-web-forms/#progressive-enhancement&quot;&gt;Progressive enhancement&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this post we work our way by progressively enhancing the form by first using only standard HTML features. This is know as &lt;strong&gt;progressive enhancement&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This tecnique suggests that &lt;strong&gt;you should make your page work with HTML first&lt;/strong&gt;, and only later add JavaScript as needed.&lt;/p&gt;
&lt;p&gt;Sources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gov.uk/service-manual/technology/using-progressive-enhancement&quot;&gt;Using progressive enhancement&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;setting-the-ground%3A-customizing-form-field-group-template-project-wide&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-accessible-web-forms/#setting-the-ground%3A-customizing-form-field-group-template-project-wide&quot;&gt;Setting the ground: customizing form field group template project-wide&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Then main requirement for this reference, in order to apply the techniques presented, is to &lt;a href=&quot;https://www.valentinog.com/blog/django-form-field-as-group/#customizing-the-form-renderer&quot;&gt;customize the form renderer of your Django project&lt;/a&gt; so that you can provide your own &lt;a href=&quot;https://www.valentinog.com/blog/django-form-field-as-group/#customizing-the-form-field-group-template-project-wide&quot;&gt;form field group template project-wide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please follow &lt;a href=&quot;https://www.valentinog.com/blog/django-form-field-as-group/&quot;&gt;Rendering form fields as group in Django&lt;/a&gt; before moving forward.&lt;/p&gt;
&lt;h2 id=&quot;provide-descriptive-field-errors-with-aria-describedby&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-accessible-web-forms/#provide-descriptive-field-errors-with-aria-describedby&quot;&gt;Provide descriptive field errors with aria-describedby&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Technique&lt;/strong&gt;: associate form fields and their respective errors with &lt;code&gt;aria-describedby&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At the time of writing using &lt;code&gt;aria-describedby&lt;/code&gt; to associate invalid fields with their error message is the most robust way for announcing errors to screen readers (see sources below).&lt;/p&gt;
&lt;p&gt;To apply this technique in Django, we augment the default Django field group template by taking control of error rendering:&lt;/p&gt;
&lt;pre class=&quot;language-diff-html&quot;&gt;&lt;code class=&quot;language-diff-html&quot;&gt;{% if field.use_fieldset %}&lt;br&gt;&lt;span class=&quot;token unchanged language-html&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &amp;lt;fieldset{% if field.help_text and field.auto_id and &quot;aria-describedby&quot; not in field.field.widget.attrs %} aria-describedby=&quot;{{ field.auto_id }}_helptext&quot;{% endif %}&gt;&lt;br&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; {% if field.label %}{{ field.legend_tag }}{% endif %}&lt;br&gt;&lt;/span&gt;{% else %}&lt;br&gt;&lt;span class=&quot;token unchanged language-html&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; {% if field.label %}{{ field.label_tag }}{% endif %}&lt;br&gt;&lt;/span&gt;{% endif %}&lt;br&gt;{% if field.help_text %}&amp;lt;div class=&quot;helptext&quot;{% if field.auto_id %} id=&quot;{{ field.auto_id }}_helptext&quot;{% endif %}&gt;{{ field.help_text|safe }}&amp;lt;/div&gt;{% endif %}&lt;br&gt;{{ field }}&lt;br&gt;&lt;span class=&quot;token deleted-sign deleted language-html&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; {{ field.errors }}&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-html&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{% if field.auto_id %}{{ field.auto_id }}_{% endif %}errorlist&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;errorlist&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  {% for error in field.errors %}&lt;br&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ error }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  {% endfor %}&lt;br&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/span&gt;{% if field.use_fieldset %}&amp;lt;/fieldset&gt;{% endif %}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we wrap field errors in a list, the same as Django does by default, but setting in addition the &lt;code&gt;id&lt;/code&gt; to the error list so that the error box will have &lt;code&gt;id_fieldname_errorlist&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With the field associated to the relevant error list, we set &lt;code&gt;aria-describedby&lt;/code&gt; in the Django form:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# books/forms.py&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; django &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; forms&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; books&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;models &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Book&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookForm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;forms&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ModelForm&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;**&lt;/span&gt;kwargs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token builtin&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__init__&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;**&lt;/span&gt;kwargs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; field_name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; field &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fields&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;            auto_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;auto_id &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; field_name&lt;br&gt;            field&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;widget&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attrs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;update&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                    &lt;span class=&quot;token string&quot;&gt;&quot;aria-describedby&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;auto_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;_errorlist &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;auto_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;_helptext&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Meta&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        model &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Book&lt;br&gt;        fields &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;publication_date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;        help_texts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token string&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Enter the title of the book.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token string&quot;&gt;&quot;publication_date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Enter the publication date.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token string&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Select the author of the book.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If a field becomes invalid after being validated on submit, Django will render the following markup :&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;id_title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Title:&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;helptext&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;id_title_helptext&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Enter the title of the book.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attr-name&quot;&gt;maxlength&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attr-name&quot;&gt;aria-describedby&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;id_title_errorlist id_title_helptext&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attr-name&quot;&gt;aria-invalid&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;id_title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;id_title_errorlist&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;This field is required.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here &lt;code&gt;aria-describedby=&amp;quot;id_title_errorlist id_title_helptext&amp;quot;&lt;/code&gt; points both to the help text and to the error box. &lt;strong&gt;This ensures that screen reader will properly announce the field errors to the user&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;testing-field-errors-and-aria-describedby&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-accessible-web-forms/#testing-field-errors-and-aria-describedby&quot;&gt;Testing field errors and aria-describedby&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Testing field errors described with &lt;code&gt;aria-describedby&lt;/code&gt; can be exercised with &lt;a href=&quot;https://github.com/valentinogagliardi/unbrowsed&quot;&gt;unbrowsed&lt;/a&gt; as follows (the example uses pytest):&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; pytest&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; unbrowsed &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; parse_html&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; get_by_role&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@pytest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mark&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;django_db&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test_book_create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Test post request to the book create view.&quot;&quot;&quot;&lt;/span&gt;&lt;br&gt;    response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;/books/create/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token string&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    dom &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; parse_html&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    get_by_role&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dom&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;textbox&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This field is required. Enter the title of the book.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.tpgi.com/describing-aria-describedby/&quot;&gt;Describing aria-describedby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cerovac.com/a11y/2024/06/support-for-aria-errormessage-is-getting-better-but-still-not-there-yet/&quot;&gt;Support for aria-errormessage is getting better, but still not there yet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://adrianroselli.com/2023/04/exposing-field-errors.html&quot;&gt;Exposing field errors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.a11y-collective.com/blog/aria-describedby/&quot;&gt;Implementing aria-describedby for Web Accessibility&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;disabling-the-default-browser-validation&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-accessible-web-forms/#disabling-the-default-browser-validation&quot;&gt;Disabling the default browser validation&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Disabling the default browser validation might seem incomprehensible to most developers, but as experience teaches, and confirmed also by multiple sources, the &lt;strong&gt;default browser validation makes more harm than good&lt;/strong&gt;. Therefore it is reasonable to disable it altogether.&lt;/p&gt;
&lt;p&gt;What to do instead?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;rely on server-side validation on submit as the default&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;progressively enhance with custom JavaScript validation if needed (shown later in the guide)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To disable the default browser validation, just put the &lt;code&gt;novalidate&lt;/code&gt; attribute on your form element:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;{% extends &quot;common/base.html&quot; %} {% block content %}&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;novalidate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  {% csrf_token %}&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ form.title.as_field_group}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ form.publication_date.as_field_group}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ form.description.as_field_group}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ form.author.as_field_group}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Submit&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;{% endblock %}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;testing-the-novalidate-attribute&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-accessible-web-forms/#testing-the-novalidate-attribute&quot;&gt;Testing the novalidate attribute&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Testing form attributes can be exercised with &lt;a href=&quot;https://github.com/valentinogagliardi/unbrowsed&quot;&gt;unbrowsed&lt;/a&gt; as follows (the example uses pytest):&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; pytest&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; unbrowsed &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; parse_html&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; get_by_role&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@pytest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mark&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;django_db&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test_book_create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Test get request to the book create view.&quot;&quot;&quot;&lt;/span&gt;&lt;br&gt;    response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/books/create/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    dom &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; parse_html&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;assert&lt;/span&gt; get_by_role&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dom&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;form&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to_have_attribute&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;novalidate&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://adrianroselli.com/2019/02/avoid-default-field-validation.html&quot;&gt;Avoid default field validation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cloudfour.com/thinks/progressively-enhanced-form-validation-part-1-html-and-css/#browser-built-in-validation-doesnt-do-everything&quot;&gt;Browser built-in validation doesn’t do everything&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.tpgi.com/required-attribute-requirements/&quot;&gt; Required attribute requirements&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;resources&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-accessible-web-forms/#resources&quot;&gt;Resources&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/valentinogagliardi/django-accessible-forms&quot;&gt;Source code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2023/02/guide-accessible-form-validation/&quot;&gt;A Guide To Accessible Form Validation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>htmx snippets and examples</title>
    <link href="https://www.valentinog.com/blog/htmx-snippets/" />
    <updated>2025-01-26T15:05:37Z</updated>
    <id>https://www.valentinog.com/blog/htmx-snippets/</id>
    <content type="html">&lt;h2 id=&quot;htmx-event-listeners&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/htmx-snippets/#htmx-event-listeners&quot;&gt;htmx event listeners&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;colon-vs-double-colon&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/htmx-snippets/#colon-vs-double-colon&quot;&gt;Colon vs double-colon&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;use-case&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/htmx-snippets/#use-case&quot;&gt;Use case&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Listening for htmx-specific events and DOM events on the same element.&lt;/p&gt;
&lt;h4 id=&quot;code&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/htmx-snippets/#code&quot;&gt;Code&lt;/a&gt;&lt;/h4&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dialog&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;hx-on:&lt;/span&gt;keydown&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;// do something here&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;br&gt;        &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;hx-on:&lt;/span&gt;:after-swap&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;// do something here&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- some other element here --&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dialog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;explanation&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/htmx-snippets/#explanation&quot;&gt;Explanation&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In htmx we can listen for DOM events with the following syntax:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;hx-on:keydown=&amp;quot;// do something here&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Notice the colon between &lt;code&gt;hx-on&lt;/code&gt; and the event name.&lt;/p&gt;
&lt;p&gt;In contrast, listening to htmx-specific events such as &lt;code&gt;afterSwap&lt;/code&gt; requires the double colon:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;hx-on::after-swap=&amp;quot;// do something here&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;h4 id=&quot;resources&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/htmx-snippets/#resources&quot;&gt;Resources&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://htmx.org/attributes/hx-on/&quot;&gt;hx-on&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://htmx.org/events/#htmx:afterSwap&quot;&gt;htmx:afterSwap&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;htmx-with-the-html-dialog-element&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/htmx-snippets/#htmx-with-the-html-dialog-element&quot;&gt;htmx with the HTML dialog element&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;opening-a-dialog%2C-removing-html-dialog-children-elements-on-esc%2C-on-close&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/htmx-snippets/#opening-a-dialog%2C-removing-html-dialog-children-elements-on-esc%2C-on-close&quot;&gt;Opening a dialog, removing HTML dialog children elements on Esc, on close&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;use-case-1&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/htmx-snippets/#use-case-1&quot;&gt;Use case&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;An HTML &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element modal that gets opened after htmx swap and that is able to remove its children elements on &lt;kbd&gt;Esc&lt;/kbd&gt; and close.&lt;/p&gt;
&lt;h4 id=&quot;code-1&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/htmx-snippets/#code-1&quot;&gt;Code&lt;/a&gt;&lt;/h4&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Trigger element --&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/book/{id}/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hx-get&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/book/{id}/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hx-target&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#dialog&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hx-swap&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;beforeend&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The naked sun&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Dialog --&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dialog&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dialog&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;hx-on:&lt;/span&gt;keydown&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;if (event.key === &#39;Escape&#39; || event.key === &#39;Esc&#39;) { disposeElements.call(this); };&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;hx-on:&lt;/span&gt;:after-swap&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;this.showModal();&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dialog&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;hx-on:&lt;/span&gt;submit&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;disposeElements.call(this);&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Close the dialog&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;some content&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;(possibly with other elements nested) here&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dialog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;disposeElements&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;dialog&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#content&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;explanation-1&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/htmx-snippets/#explanation-1&quot;&gt;Explanation&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This dialog is always present in the DOM, but initially hidden as per the default. When the user clicks the trigger element, htmx swaps the partial HTML received from the endpoint declared in &lt;code&gt;hx-get&lt;/code&gt;, and puts the content into the dialog.&lt;/p&gt;
&lt;p&gt;After the swap, the modal opens itself:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;hx-on::after-swap=&amp;quot;this.showModal();&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Please note that using &lt;code&gt;showModal()&lt;/code&gt; is preferred over the &lt;code&gt;open&lt;/code&gt; attribute of the dialog since modals opened with &lt;code&gt;showModal()&lt;/code&gt; can be closed with &lt;kbd&gt;Esc&lt;/kbd&gt;.&lt;/p&gt;
&lt;p&gt;When the user closes the dialog by clicking &lt;code&gt;&amp;lt;button&amp;gt;Close the dialog&amp;lt;/button&amp;gt;&lt;/code&gt;, the modal closes via &lt;code&gt;method=&amp;quot;dialog&amp;quot;&lt;/code&gt; on the &lt;code&gt;form&lt;/code&gt; element. This triggers a &lt;code&gt;submit&lt;/code&gt; event on the form, where we dispose of the content:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;hx-on:submit=&amp;quot;disposeElements.call(this);&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;When the user closes the dialog by pressing the &lt;kbd&gt;Esc&lt;/kbd&gt; key, this triggers a &lt;code&gt;keydown&lt;/code&gt; event where we dispose of the content:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;hx-on:keydown=&amp;quot;if (event.key === &#39;Escape&#39; || event.key === &#39;Esc&#39;) { disposeElements.call(this); };&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;h4 id=&quot;resources-1&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/htmx-snippets/#resources-1&quot;&gt;Resources&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog&quot;&gt;the Dialog element&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://htmx.org/attributes/hx-get/&quot;&gt;hx-get&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Rendering form fields as group in Django</title>
    <link href="https://www.valentinog.com/blog/django-form-field-as-group/" />
    <updated>2025-01-24T18:21:43Z</updated>
    <id>https://www.valentinog.com/blog/django-form-field-as-group/</id>
    <content type="html">&lt;h2 id=&quot;field-groups-in-django&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-form-field-as-group/#field-groups-in-django&quot;&gt;Field groups in Django&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A welcome addition in Django 5.0, the concept of &lt;strong&gt;field groups&lt;/strong&gt; makes a lot easier to customize the layout of our Django forms.&lt;/p&gt;
&lt;p&gt;In the past, to take control of field layout, developers had to render field labels, help text, errors and fields one by one (or resort to use an external library).&lt;/p&gt;
&lt;p&gt;Field group solve that, as we will see in a moment.&lt;/p&gt;
&lt;p&gt;In this brief post I base my examples over a Django &lt;code&gt;library&lt;/code&gt; app, with a model named &lt;code&gt;Book&lt;/code&gt;. You can look up the structure in &lt;a href=&quot;https://github.com/valentinogagliardi/django-testing-cheat-sheet&quot;&gt;this repo&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;customizing-the-form-renderer&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-form-field-as-group/#customizing-the-form-renderer&quot;&gt;Customizing the form renderer&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As a first step to opt-in into Django form field groups, we need to customize the form rendered. This can be done by creating your custom renderer in a file named &lt;code&gt;renderers.py&lt;/code&gt;, ideally in a &lt;code&gt;common&lt;/code&gt; app in your project.&lt;/p&gt;
&lt;p&gt;The custom form rendered must inherit from &lt;code&gt;TemplatesSetting&lt;/code&gt; in order to make the field template swappable:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; django&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;forms&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;renderers &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; TemplatesSetting&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FormRenderer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;TemplatesSetting&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    field_template_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;common/field.html&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The renderer can then be used in &lt;code&gt;settings.py&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;INSTALLED_APPS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# Remember always to include django.forms&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;django.forms&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;common&#39;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;br&gt;FORM_RENDERER &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;common.renderers.FormRenderer&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pay attention: &lt;strong&gt;in order to be able to customize the field group template&lt;/strong&gt; in the following section, &lt;code&gt;django.forms&lt;/code&gt; must be in our &lt;code&gt;INSTALLED_APPS&lt;/code&gt;!&lt;/p&gt;
&lt;h2 id=&quot;customizing-the-form-field-group-template-project-wide&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-form-field-as-group/#customizing-the-form-field-group-template-project-wide&quot;&gt;Customizing the form field group template project-wide&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that we declared our custom form renderer, we can proceed to customize the form field template by creating a new file named &lt;code&gt;field.html&lt;/code&gt; in &lt;code&gt;common/templates/common&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-django&quot;&gt;&lt;code class=&quot;language-django&quot;&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;use_fieldset&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;fieldset&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;help_text&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;auto_id&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aria-describedby&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;attrs&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-describedby&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;auto_id&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;_helptext&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;legend_tag&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;label_tag&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;help_text&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;helptext&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;auto_id&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;auto_id&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;_helptext&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token filter function&quot;&gt;safe&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;errors&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;use_fieldset&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;fieldset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This template is taken from &lt;a href=&quot;https://github.com/django/django/blob/main/django/forms/templates/django/forms/field.html&quot;&gt;Django itself&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can now enrich it with any desired change, or leave it as it is.&lt;/p&gt;
&lt;p&gt;Let&#39;s now create the view to handle the form.&lt;/p&gt;
&lt;h2 id=&quot;rendering-fields%3A-the-create-view&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-form-field-as-group/#rendering-fields%3A-the-create-view&quot;&gt;Rendering fields: the create view&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With the template in place, we can now create a Django view to display the form. I use a &lt;code&gt;CreateView&lt;/code&gt; in this example, which generates its form automatically. If you need more customization over widgets or over the validation, you can declare your own &lt;code&gt;ModelForm&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&#39;s the view to create books:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookCreateView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;CreateView&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    model &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Book&lt;br&gt;    fields &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;authors&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;    success_url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: your custom field template will work well even with a regular Django &lt;code&gt;Form&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With the view and the form in place, we can render the form in our template.&lt;/p&gt;
&lt;h2 id=&quot;rendering-fields%3A-the-form-template&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-form-field-as-group/#rendering-fields%3A-the-form-template&quot;&gt;Rendering fields: the form template&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We are now ready to render our Django form in a template. Here it is:&lt;/p&gt;
&lt;pre class=&quot;language-django&quot;&gt;&lt;code class=&quot;language-django&quot;&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;library/base.html&quot;&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;novalidate&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;csrf_token&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-group&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;as_field_group&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-group&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;authors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;as_field_group&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;btn btn-primary&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Save&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;endblock&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we no longer need to call field attributes one by one to group the field into a containing HTML element.&lt;/p&gt;
&lt;p&gt;We can just call &lt;code&gt;form.field_name.as_field_group&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-django&quot;&gt;&lt;code class=&quot;language-django&quot;&gt;        ...&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-group&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;as_field_group&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        ...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isn&#39;t that almost magic?&lt;/p&gt;
&lt;p&gt;Calling &lt;code&gt;form.field_name.as_field_group&lt;/code&gt; will render &lt;strong&gt;the label, any help text, the field error list, and the field itself&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Here&#39;s the HTML for the title field after a form submit:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-group&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;id_title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Title:&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;errorlist&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;This field is required.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;maxlength&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;100&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-invalid&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;id_title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note: if you don&#39;t see any effect after customizing the field template in your project, check that&lt;/strong&gt; &lt;code&gt;django.forms&lt;/code&gt; is declared in &lt;code&gt;INSTALLED_APPS&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;reusable-form-templates&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-form-field-as-group/#reusable-form-templates&quot;&gt;Reusable form templates&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Keep in mind that you can not only customize the field template, but also take control of the form template, if you need to do so. Check &lt;a href=&quot;https://docs.djangoproject.com/en/5.1/topics/forms/#reusable-form-templates&quot;&gt;reusable form templates for more&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-form-field-as-group/#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Reusable form field groups in Django make a lot easier to take control of form field rendering&lt;/strong&gt; without reaching for external libraries.&lt;/p&gt;
&lt;p&gt;Available since Django 5.0 &lt;a href=&quot;https://docs.djangoproject.com/en/5.1/releases/5.0/#simplified-templates-for-form-field-rendering&quot;&gt;form fields can render as a group&lt;/a&gt;, and have their template overridden in different ways.&lt;/p&gt;
&lt;p&gt;This can happen at various levels as described also in &lt;a href=&quot;https://docs.djangoproject.com/en/5.1/topics/forms/#reusable-field-group-templates&quot;&gt;the documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Progressive enhancement in Django with htmx</title>
    <link href="https://www.valentinog.com/blog/django-progressive-enhancement-htmx/" />
    <updated>2025-01-22T17:05:12Z</updated>
    <id>https://www.valentinog.com/blog/django-progressive-enhancement-htmx/</id>
    <content type="html">&lt;p&gt;Welcome reader to one of those articles &lt;strong&gt;where it seems I&#39;m stating the obvious&lt;/strong&gt;! But frontend developers constantly forget the most obvious things to do, so here I am.&lt;/p&gt;
&lt;p&gt;I&#39;ve always been a proponent of &lt;a href=&quot;https://www.gov.uk/service-manual/technology/using-progressive-enhancement&quot;&gt;progressive enhancement&lt;/a&gt;, and in general of keeping things simple. However, building things this way might not always be possible for a number of reasons.&lt;/p&gt;
&lt;p&gt;When finally the stars align and you get the chance to truly adopt progressive enhancement, patterns start to emerge quickly, and things finally click (no pun intended)!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In Django, for example, adopting progressive enhancement&lt;/strong&gt; with htmx might be &lt;strong&gt;easier than you think&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Let&#39;s see!&lt;/p&gt;
&lt;h3 id=&quot;progressive-enhancement-in-django-with-htmx%3A-the-template&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-progressive-enhancement-htmx/#progressive-enhancement-in-django-with-htmx%3A-the-template&quot;&gt;Progressive enhancement in Django with htmx: the template&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Consider the following example. To start with you&#39;ve got a link augmented with &lt;code&gt;hx-get&lt;/code&gt;. The &lt;strong&gt;conscious developer&lt;/strong&gt; should ask itself first of all: &lt;strong&gt;what happens if JavaScript doesn&#39;t work, or if it&#39;s disabled at all in the user browser&lt;/strong&gt;? For example, this won&#39;t work when the user clicks the link:&lt;/p&gt;
&lt;pre class=&quot;language-django&quot;&gt;&lt;code class=&quot;language-django&quot;&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;book-detail&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;pk&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;book_detail&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hx-get&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;book_detail&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This &lt;strong&gt;instead will work, providing progressive enhancement to our users&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-django&quot;&gt;&lt;code class=&quot;language-django&quot;&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;token tag keyword&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;book-detail&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;pk&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;book_detail&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;%}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;book_detail&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hx-get&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;book_detail&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token django language-django&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Magic!&lt;/p&gt;
&lt;p&gt;If &lt;strong&gt;the user clicks the link and JavaScript is disabled, the browser will happily follow the link, providing the native link experience for the user&lt;/strong&gt;. If JavaScript is enabled instead, htmx will get into action, fetch the content from the url provided in &lt;code&gt;hx-get&lt;/code&gt;, and swap the latter into the given target.&lt;/p&gt;
&lt;p&gt;Let&#39;s see into the next section how to handle the Django view.&lt;/p&gt;
&lt;h3 id=&quot;progressive-enhancement-in-django-with-htmx%3A-the-view&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-progressive-enhancement-htmx/#progressive-enhancement-in-django-with-htmx%3A-the-view&quot;&gt;Progressive enhancement in Django with htmx: the view&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To make the magic of progressive enhancement happen in Django we need to serve different templates, depending on whether the request comes from JavaScript (via htmx) or from a regular browser HTTP request.&lt;/p&gt;
&lt;p&gt;Consider the following Django app structure:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;books/templates&lt;br&gt;└── books&lt;br&gt;    ├── partials&lt;br&gt;    │   └── book.html&lt;br&gt;    ├── books_detail.html&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The arrangement here sees &lt;code&gt;books_detail.html&lt;/code&gt; including the partial:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;{# here you would extend from base.html #}&lt;br&gt;&lt;br&gt;{% block content %}&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  {% include &quot;books/partials/book.html&quot; %}&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;{% endblock content %}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&#39;s instead the partial &lt;code&gt;book.html&lt;/code&gt; (where &lt;code&gt;object&lt;/code&gt; is a book):&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt; {{ object.title }}&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally here&#39;s the view:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# ... your imports here&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookDetail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;DetailView&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    model &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Book&lt;br&gt;    template_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;books/book_detail.html&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;**&lt;/span&gt;kwargs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;htmx&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;            self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;template_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;books/partial/book.html&quot;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;**&lt;/span&gt;kwargs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The way this works is that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;when an &lt;strong&gt;full HTTP page request&lt;/strong&gt; hits the view, Django will serve &lt;code&gt;books_detail.html&lt;/code&gt; with a full page refresh like the good ol&#39; days&lt;/li&gt;
&lt;li&gt;when an &lt;strong&gt;htmx request via JavaScript&lt;/strong&gt; hits the view, Django will serve the &lt;code&gt;book.html&lt;/code&gt; partial, and htmx will do its job of swapping the partial content into the page&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&#39;m using &lt;a href=&quot;https://github.com/adamchainz/django-htmx&quot;&gt;django-htmx&lt;/a&gt; here.&lt;/p&gt;
&lt;p&gt;Was that hard? Not at all.&lt;/p&gt;
&lt;p&gt;Did we create duplication? No, thanks to Django template inheritance.&lt;/p&gt;
&lt;p&gt;Did I discover the obvious? Maybe! :-)&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://www.valentinog.com/blog/testing-django/&quot;&gt;A cheat sheet of common testing recipes for Django applications&lt;/a&gt; for suggestions on how to test this Django view.&lt;/p&gt;
&lt;h3 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-progressive-enhancement-htmx/#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In this post I presented a very simple technique to ensure that our Django interfaces works even when JavaScript is disabled.&lt;/p&gt;
&lt;p&gt;If the request comes from htmx, it means JavaScript is enabled and functioning in the frontend: in our Django views &lt;strong&gt;we can serve a partial template that will be swapped into the DOM&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If the request does not come from JavaScript instead, &lt;strong&gt;we serve the full page and keep the experience working&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;h3 id=&quot;resources&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-progressive-enhancement-htmx/#resources&quot;&gt;Resources&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://thehistoryoftheweb.com/the-inclusive-web-of-progressive-enhancement/&quot;&gt;Progressive enhancement brings everyone in&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://alistapart.com/article/understandingprogressiveenhancement/&quot;&gt;Understanding Progressive Enhancement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gov.uk/service-manualtechnology/using-progressive-enhancement&quot;&gt;Building a robust frontend using progressive enhancement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://htmx.org/&quot;&gt;htmx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/adamchainz/django-htmx&quot;&gt;django-htmx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Exploring techniques for untrusted Python code execution in agentic workflows</title>
    <link href="https://www.valentinog.com/blog/caging-the-agent/" />
    <updated>2024-12-06T18:41:03Z</updated>
    <id>https://www.valentinog.com/blog/caging-the-agent/</id>
    <content type="html">&lt;p&gt;If you ask me, one of the most compelling use cases for &lt;strong&gt;LLMs is the ability to let them invoke tools&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;LLM parlance, tools are functions that the model can invoke at will.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;type-of-tools&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/caging-the-agent/#type-of-tools&quot;&gt;Type of tools&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://www.valentinog.com/blog/caging-the-agent/defining-llm-tools.001.png&quot; alt=&quot;Defining LLM tools&quot;&gt;&lt;/p&gt;
&lt;p&gt;I like to distinguish between (for now) two type of tools the LLM can invoke:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;safe(ish)&lt;/strong&gt; tools&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;unsafe&lt;/strong&gt; tools&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Safe&lt;/strong&gt; tools are functions written by the agent developer. These functions are (hopefully) well tested and devoid of security risks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Unsafe&lt;/strong&gt; tools on the other hand are function completely generated by the LLM, and then invoked by the very same. As we can easily imagine, this poses a number of security implications and risks.&lt;/p&gt;
&lt;h2 id=&quot;generating-and-executing-code&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/caging-the-agent/#generating-and-executing-code&quot;&gt;Generating and executing code&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://arxiv.org/abs/2401.07339&quot;&gt;&lt;em&gt;Large Language Models (LLMs) have shown promise in automated code generation but typically excel only in simpler tasks such as generating standalone code units&lt;/em&gt;&lt;/a&gt;. That is, for simpler tasks, such as creating a doc or odt files, LLM are able to generate proper code (although not without guidance).&lt;/p&gt;
&lt;p&gt;As of today, &lt;strong&gt;proper prompting&lt;/strong&gt;, or the use of few-shot examples is necessary to have the model generate working code through a trial and error process.&lt;/p&gt;
&lt;h3 id=&quot;risks&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/caging-the-agent/#risks&quot;&gt;Risks&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.valentinog.com/blog/caging-the-agent/llm-code-execution-risks.001.png&quot; alt=&quot;Non-exhaustive risks of LLM generated code&quot;&gt;&lt;/p&gt;
&lt;p&gt;LLMs can generate and execute their own code. At what cost? Here&#39;s a non-exhaustive list of the risks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;escaping&lt;/li&gt;
&lt;li&gt;data loss&lt;/li&gt;
&lt;li&gt;data leak&lt;/li&gt;
&lt;li&gt;resource exhaustion&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Data leaks and resource exhaustion are particulary terrifying.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Resource exhaustion&lt;/strong&gt; can manifest itself in a number of ways since the model might generate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;code that gets stuck in infinite loops&lt;/li&gt;
&lt;li&gt;regular expressions vulnerable to catastrophic backtracking&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;mitigations&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/caging-the-agent/#mitigations&quot;&gt;Mitigations&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;monitoring&lt;/li&gt;
&lt;li&gt;human supervision&lt;/li&gt;
&lt;li&gt;limiting access to data&lt;/li&gt;
&lt;li&gt;data anonymization&lt;/li&gt;
&lt;li&gt;preemptive testing&lt;/li&gt;
&lt;li&gt;sandboxing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Human supervision&lt;/strong&gt; is particularly challenging. An agent interacting with non technical people cannot be easily scrutinized for insecure code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Preemptive testing&lt;/strong&gt; is a technique which can potentially limit damage. The idea is to test the code generated by the LLM before executing it.&lt;/p&gt;
&lt;p&gt;Considering the stakes, above all the mitigations proposed, &lt;strong&gt;sandboxing&lt;/strong&gt; of the generated code is the most promising technique. However, &lt;strong&gt;&lt;a href=&quot;https://lwn.net/Articles/574215/&quot;&gt;sandboxing in Python is notoriously hard&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;executing-code%2C-the-naive-way&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/caging-the-agent/#executing-code%2C-the-naive-way&quot;&gt;Executing code, the naive way&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It&#39;s easy to fall in the trap to directly execute the code generated by an LLM. Think of this example:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@tool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;execute_python_code&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;execute_python_code&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code_string&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Execute the given Python code string and return the local variables.&quot;&quot;&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        global_vars &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;        local_vars &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code_string&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; global_vars&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; local_vars&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; Exception &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Error executing code: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is highly insecure and risky.&lt;/p&gt;
&lt;h2 id=&quot;executing-code%2C-better-ways&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/caging-the-agent/#executing-code%2C-better-ways&quot;&gt;Executing code, better ways&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are a number of ways to execute untrusted (Python in this case) code. While sandboxing Python is pratically impossible (see resources), there are a number of ways to control the potential damage.&lt;/p&gt;
&lt;h3 id=&quot;huggingface-python-interpreter-tool&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/caging-the-agent/#huggingface-python-interpreter-tool&quot;&gt;HuggingFace Python interpreter tool&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This Python interpreter from HuggingFace uses &lt;a href=&quot;https://docs.python.org/3/library/ast.html&quot;&gt;ast&lt;/a&gt; under the hood. It has the ability to scan the code string with a great degree of accuracy. It prevents infinite loops and has the ability to restrict imports. Here&#39;s an example usage:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; transformers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;agents &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; PythonInterpreterTool&lt;br&gt;&lt;br&gt;python_repl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; PythonInterpreterTool&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;authorized_imports&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;odf&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pathlib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generate_odt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code_string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Execute the given Python code to save an odt file by using a Python REPL.&quot;&quot;&quot;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; python_repl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;forward&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code_string&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;azure-container-apps-dynamic-sessions&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/caging-the-agent/#azure-container-apps-dynamic-sessions&quot;&gt;Azure Container Apps dynamic sessions&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One interesting technique for running untrusted Python code is offered by &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/container-apps/sessions&quot;&gt;Azure Container Apps dynamic sessions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This service provides strong isolated sandboxing environments, ideal for running LLM-generated code. Here&#39;s an example usage in Langchain:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; os&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; langchain_azure_dynamic_sessions &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; SessionsPythonREPLTool&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; langchain_core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tools &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; tool&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; dotenv &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; load_dotenv&lt;br&gt;&lt;br&gt;load_dotenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AZURE_TENANT_ID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AZURE_TENANT_ID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AZURE_CLIENT_ID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AZURE_CLIENT_ID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AZURE_CLIENT_SECRET&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AZURE_CLIENT_SECRET&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;repl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; SessionsPythonREPLTool&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;    pool_management_endpoint&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;POOL_MANAGEMENT_ENDPOINT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@tool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;generate-odt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generate_odt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code_string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Execute the given Python code to save an odt file in a Python REPL.&quot;&quot;&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    output_dir &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;files&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    properties &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; repl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;execute&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code_string&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; properties &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; properties&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        filename &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; properties&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;            output_dir&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mkdir&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;exist_ok&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;            repl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;download_file&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;                remote_file_path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; local_file_path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;output_dir &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; filename&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; output_dir &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; filename&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; HTTPError &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Error downloading file: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; properties&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example we use the Azure Container Apps dynamic sessions REPL to run code generated by the LLM. The tool is able to execute the provided code in a sandboxed environment and then download the resulting file.&lt;/p&gt;
&lt;h4 id=&quot;restrictedpython&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/caging-the-agent/#restrictedpython&quot;&gt;RestrictedPython&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://restrictedpython.readthedocs.io/en/latest/index.html&quot;&gt;RestrictedPython&lt;/a&gt; helps reducing the risk surface when running untrusted code, but it&#39;s not enough alone. For example, &lt;a href=&quot;https://github.com/tjmlabs/AgentRun&quot;&gt;AgentRun&lt;/a&gt; use a combination of Docker and Restricted Python to execute untrusted code.&lt;/p&gt;
&lt;h2 id=&quot;resources&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/caging-the-agent/#resources&quot;&gt;Resources&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;llm-and-tools&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/caging-the-agent/#llm-and-tools&quot;&gt;LLM and tools&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://platform.openai.com/docs/guides/function-calling&quot;&gt;Function calling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/anthropics/courses/blob/master/tool_use/README.md&quot;&gt;Anthropic&#39;s comprehensive tool use tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-execution-and-sandboxing-in-python&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/caging-the-agent/#code-execution-and-sandboxing-in-python&quot;&gt;Code execution and sandboxing in Python&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://lwn.net/Articles/574215/&quot;&gt;The failure of pysandbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://doc.pypy.org/en/latest/sandbox.html&quot;&gt;PyPy&#39;s sandboxing features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://restrictedpython.readthedocs.io/en/latest/index.html&quot;&gt;RestrictedPython&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Simon Willison&#39;s has a lot of &lt;a href=&quot;https://simonwillison.net/tags/code-interpreter/&quot;&gt;great resources about code execution&lt;/a&gt; in the context of LLM systems&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>django-filter: filtering a foreign key model property</title>
    <link href="https://www.valentinog.com/blog/django-filter-foreign-key/" />
    <updated>2024-09-06T11:00:37Z</updated>
    <id>https://www.valentinog.com/blog/django-filter-foreign-key/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://django-filter.readthedocs.io/en/stable/&quot;&gt;django-filter&lt;/a&gt; is a fantastic library that makes easy to add complex filters in Django. While it requires very little code to get up and running, it can be a bit tricky to figure out how to do more non-standard things, like for example &lt;strong&gt;filtering against a foreign key model property&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In this brief post we&#39;ll see with an example how I&#39;ve achieved this.&lt;/p&gt;
&lt;h2 id=&quot;the-models&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-filter-foreign-key/#the-models&quot;&gt;The models&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Consider two simple Django models, &lt;code&gt;Book&lt;/code&gt; and &lt;code&gt;Author&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; django&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;db &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; models&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;models&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Model&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; models&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CharField&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;max_length&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    author &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; models&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ForeignKey&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Author&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; on_delete&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;models&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CASCADE&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__str__&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;models&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Model&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    first_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; models&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CharField&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;max_length&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    last_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; models&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CharField&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;max_length&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@property&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;full_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;last_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__str__&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;full_name&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Book&lt;/code&gt; has a &lt;code&gt;ForeignKey&lt;/code&gt; to &lt;code&gt;Author&lt;/code&gt;, and &lt;code&gt;Author&lt;/code&gt; has a &lt;code&gt;full_name&lt;/code&gt; property that returns the full name of the author.&lt;/p&gt;
&lt;p&gt;My use case is to be able to filter the &lt;code&gt;Book&lt;/code&gt; list by the &lt;code&gt;Author&lt;/code&gt; &lt;code&gt;full_name&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;Django filter would make easy to filter against a model field, that is, &lt;strong&gt;things that are actual database columns&lt;/strong&gt;, like &lt;code&gt;author__first_name&lt;/code&gt; or &lt;code&gt;author__last_name&lt;/code&gt;, but filtering against a model property like &lt;code&gt;author__full_name&lt;/code&gt; is not so straightforward.&lt;/p&gt;
&lt;p&gt;In other words, we would like to hit our page at &lt;code&gt;/books/?author_full_name=clarke&lt;/code&gt; and get all the books written by Arthur C. Clarke.&lt;/p&gt;
&lt;h2 id=&quot;the-filter&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-filter-foreign-key/#the-filter&quot;&gt;The filter&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To clarify things better, here&#39;s how the filter would look like if we were filtering against a model field:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; django_filters &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; FilterSet&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;models &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Book&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookFilterSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;FilterSet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Meta&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        model &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Book&lt;br&gt;        fields &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;author__last_name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will work fine for hitting &lt;code&gt;/books/?author_first_name=clarke&lt;/code&gt;. What happens instead if we try to filter against a model property? Like this:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; django_filters &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; FilterSet&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;models &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Book&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookFilterSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;FilterSet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Meta&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        model &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Book&lt;br&gt;        fields &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;author__full_name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Django filter will complain with this error:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;TypeError&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Meta.fields&#39;&lt;/span&gt; must &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; contain non&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;model field names&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; author__full_name&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s see how to fix this!&lt;/p&gt;
&lt;h2 id=&quot;fixing-the-filter%2C-wiring-up-the-view-and-the-queryset&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-filter-foreign-key/#fixing-the-filter%2C-wiring-up-the-view-and-the-queryset&quot;&gt;Fixing the filter, wiring up the view and the queryset&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To make Django filter work with a model property, first off we need to define a custom filter for that property:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; django_filters &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; FilterSet&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; CharFilter&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;models &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Book&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookFilterSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;FilterSet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    author_full_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; CharFilter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lookup_expr&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;icontains&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; label&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Author full name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Meta&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        model &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Book&lt;br&gt;        fields &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;other_fields_here&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, the &lt;code&gt;BookFilterSet&lt;/code&gt; is instructed to use a &lt;code&gt;CharFilter&lt;/code&gt; for the &lt;code&gt;author_full_name&lt;/code&gt; property. In this example, we also use the &lt;code&gt;icontains&lt;/code&gt; lookup expression.&lt;/p&gt;
&lt;p&gt;Next up, we wire up the Django view alongside with the &lt;code&gt;get_queryset&lt;/code&gt; method. &lt;strong&gt;The &amp;quot;trick&amp;quot; to make Django filter work with a model property is to use an annotation to add a new field to the queryset&lt;/strong&gt;, that is, the &lt;code&gt;author_full_name&lt;/code&gt; field:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; django&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;models &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; F&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; django&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;models &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Value &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; V&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; django&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;models&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;functions &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Concat&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; django_filters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;views &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; FilterView&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;models &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Book&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;filters &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; BookFilterSet&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BookList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;FilterView&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    model &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Book&lt;br&gt;    filterset_class &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; BookFilterSet&lt;br&gt;    template_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;books/book_list.html&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_queryset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token builtin&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_queryset&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;annotate&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;                author_full_name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;Concat&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;                    F&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;author__first_name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; V&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; F&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;author__last_name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&#39;s a breakdown of the queryset:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;super().get_queryset()&lt;/code&gt; gets the base queryset&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.annotate()&lt;/code&gt; adds a new field to the queryset &lt;code&gt;author_full_name&lt;/code&gt;, by concatenating &lt;code&gt;author__first_name&lt;/code&gt; and &lt;code&gt;author__last_name&lt;/code&gt; fields.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With this change in place, Django filter is now able to filter against the &lt;code&gt;author_full_name&lt;/code&gt; property. This way, we can hit &lt;code&gt;/books/?author_full_name=clarke&lt;/code&gt; in Django, and have the filter work properly.&lt;/p&gt;
&lt;p&gt;Here&#39;s the query again:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_queryset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token builtin&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_queryset&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;annotate&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;                author_full_name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;Concat&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;                    F&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;author__first_name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; V&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; F&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;author__last_name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice the use of &lt;code&gt;F()&lt;/code&gt; and &lt;code&gt;V()&lt;/code&gt; expressions. &lt;code&gt;F()&lt;/code&gt; is used to reference a field in the database. The database function &lt;code&gt;Concat()&lt;/code&gt; instead is used to concatenate the fields. If you want, you can read more about expressions in &lt;a href=&quot;https://docs.djangoproject.com/en/5.1/ref/models/expressions/&quot;&gt;Query Expressions&lt;/a&gt;, and functions in &lt;a href=&quot;https://docs.djangoproject.com/en/5.1/ref/models/database-functions/&quot;&gt;Database Functions&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-filter-foreign-key/#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://django-filter.readthedocs.io/en/stable/&quot;&gt;django-filter&lt;/a&gt; is a fantastic, easy to use, well-documented library for filtering in Django. While it works almost out of the box with model fields, it requires a bit of extra work to handle Django model properties.&lt;/p&gt;
&lt;p&gt;In this post, we saw how achieve that by &lt;strong&gt;using an annotation to add a new field to the queryset, and then using that field in the filter&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Another alternative, if you don&#39;t mind using a third party library, is to use &lt;a href=&quot;https://github.com/W1ldPo1nter/django-queryable-properties&quot;&gt;django-queryable-properties&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Implementing the strategy pattern in Django with JavaScript import maps</title>
    <link href="https://www.valentinog.com/blog/django-import-maps/" />
    <updated>2023-09-08T14:02:45Z</updated>
    <id>https://www.valentinog.com/blog/django-import-maps/</id>
    <content type="html">&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Strategy_pattern&quot;&gt;strategy pattern&lt;/a&gt; is a software design pattern which lets you swap an algorithm at runtime. From wikipedia:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Lately I&#39;ve been experimenting with a new web feature, &lt;strong&gt;import maps, and it turns out, they seem to play nicely in Django to implement the strategy pattern for JavaScript code in the frontend&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;With simple examples, we&#39;re going to introduce the motivation behind this technique, and the actual implementation.&lt;/p&gt;
&lt;h2 id=&quot;the-need-for-the-strategy-pattern&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-import-maps/#the-need-for-the-strategy-pattern&quot;&gt;The need for the strategy pattern&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s imagine a &lt;strong&gt;generic piece of JavaScript code in our Django project which needs to behave differently depending on the context where it&#39;s executed&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Let&#39;s imagine that if I&#39;m on a page with &lt;code&gt;books/&lt;/code&gt; in the pathname, I want to write &amp;quot;Hello import maps from the books app!&amp;quot;, whereas when I&#39;m in the &lt;code&gt;authors/&lt;/code&gt; path I want to write &amp;quot;Hello import maps from the authors app!&amp;quot;.&lt;/p&gt;
&lt;p&gt;This can be achieved in a number of ways.&lt;/p&gt;
&lt;p&gt;The suboptimal way is to have our code take decisions with a series of &lt;code&gt;if&lt;/code&gt; statements. Consider the following example:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;renderText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pathname &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;books/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;p&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello import maps from the books app!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pathname &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;authors/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// do something else&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without going too far, you can easily guess what this code can become. &lt;strong&gt;Is there a better solution&lt;/strong&gt;?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What if instead we can import one or more functions (or variables) from a &amp;quot;swappable&amp;quot; JavaScript import&lt;/strong&gt;? Consider the following code:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; renderText &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;strategy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;renderText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we import &lt;code&gt;renderText&lt;/code&gt; from the &lt;code&gt;strategy&lt;/code&gt; JavaScript module.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The calling code does not know where &lt;code&gt;strategy&lt;/code&gt; is coming from. It has only to execute&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It&#39;s worth mentioning that here we are talking about &lt;strong&gt;&lt;a href=&quot;https://www.valentinog.com/blog/es-modules/&quot;&gt;JavaScript ES modules&lt;/a&gt;&lt;/strong&gt;, denoted by the syntax &lt;code&gt;import ... from &amp;quot;module-name&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It&#39;s also important to understand that &lt;strong&gt;ES modules are static, that is, they cannot be changed at runtime&lt;/strong&gt;, and that up until recently, &lt;strong&gt;bare imports in the browser where not supported&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;To make things clearer, up until recently, the syntax &lt;code&gt;import { renderText } from &amp;quot;strategy&amp;quot;&lt;/code&gt; &lt;strong&gt;wasn&#39;t valid in the browser&lt;/strong&gt;, being &lt;code&gt;&amp;quot;strategy&amp;quot;&lt;/code&gt; a &lt;em&gt;bare&lt;/em&gt; import. This syntax was supported only by module bundlers like &lt;a href=&quot;https://www.valentinog.com/blog/webpack/&quot;&gt;webpack&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, recently, &lt;strong&gt;with the introduction of &lt;a href=&quot;https://github.com/WICG/import-maps&quot;&gt;JavaScript import maps&lt;/a&gt;, born to allow control over what URL gets fetched by JavaScript &lt;code&gt;import&lt;/code&gt; statements, we have a way to use bare imports in the browser, and also to potentially swap the import URL at runtime&lt;/strong&gt; as we will see in a moment.&lt;/p&gt;
&lt;p&gt;Pair that &lt;strong&gt;with the flexiblity of the Django templating system, and we can now have a neat way to implement a simple version of the strategy pattern in our JavaScript code&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Let&#39;s see this technique in practice!&lt;/p&gt;
&lt;h2 id=&quot;implementing-the-strategy-pattern-with-import-maps&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-import-maps/#implementing-the-strategy-pattern-with-import-maps&quot;&gt;Implementing the strategy pattern with import maps&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Implementing the strategy pattern in Django with JavaScript import maps exploits the &lt;strong&gt;ability of Django templates to extend and overwrite a given template block&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In brief, here&#39;s how it works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;a child template extends a parent template&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;the child template overrides the js block to declare its import map&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&#39;s see it in practice. For our example we have a Django project composed of three apps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;core&lt;/code&gt;, containing the base template and the base JavaScript file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;authors&lt;/code&gt;, extending the base template from &lt;code&gt;core&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;books&lt;/code&gt;, extending the base template from &lt;code&gt;core&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;the-core-app&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-import-maps/#the-core-app&quot;&gt;The core app&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The folder structure of the &lt;code&gt;core&lt;/code&gt; app:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;core&lt;br&gt;├── admin.py&lt;br&gt;├── apps.py&lt;br&gt;├── __init__.py&lt;br&gt;├── migrations&lt;br&gt;│   ├── __init__.py&lt;br&gt;├── models.py&lt;br&gt;├── static&lt;br&gt;│   └── core&lt;br&gt;│       └── js&lt;br&gt;│           └── Base.js&lt;br&gt;├── templates&lt;br&gt;│   └── core&lt;br&gt;│       └── base.html&lt;br&gt;├── tests.py&lt;br&gt;└── views.py&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The base Django template &lt;code&gt;core/templates/core/base.html&lt;/code&gt; declares &lt;strong&gt;Django template blocks&lt;/strong&gt; that inheritors can override, namely &lt;code&gt;content&lt;/code&gt; and &lt;code&gt;js&lt;/code&gt;. In addition, it loads a JavaScript module:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;{% load static %}&lt;br&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;en&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;charset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;UTF-8&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The base template&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;{% block content %}{% endblock %}&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;{% block js %}&lt;br&gt;	&amp;lt;script type=&quot;module&quot; src=&quot;{% static &quot;core/js/Base.js&quot; %}&quot;&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;{% endblock %}&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In detail:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;script type=&quot;module&quot; src=&quot;{% static &quot;core/js/Base.js&quot; %}&quot;&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the base JavaScript file, &lt;strong&gt;a piece of logic that will behave differently depending on the page where it&#39;s called&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The base JavaScript file &lt;code&gt;core/static/core/js/Base.js&lt;/code&gt; imports and executes any function (or constant) it needs from a &lt;code&gt;strategy&lt;/code&gt;, without even knowing where this file is. Here&#39;s the file content:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; renderText &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;strategy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;renderText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s now see the two other Django apps.&lt;/p&gt;
&lt;p&gt;NOTE: the module import name can be anything you want, not necessarily &lt;code&gt;&amp;quot;strategy&amp;quot;&lt;/code&gt;, as long as you reference the same name in child templates as well.&lt;/p&gt;
&lt;h3 id=&quot;the-authors-app&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-import-maps/#the-authors-app&quot;&gt;The authors app&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;authors&lt;/code&gt; app is one of the apps using the base template from &lt;code&gt;core&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The folder structure of the &lt;code&gt;authors&lt;/code&gt; app:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;authors&lt;br&gt;├── admin.py&lt;br&gt;├── apps.py&lt;br&gt;├── __init__.py&lt;br&gt;├── migrations&lt;br&gt;│   ├── __init__.py&lt;br&gt;├── models.py&lt;br&gt;├── static&lt;br&gt;│   └── authors&lt;br&gt;│       └── js&lt;br&gt;│           └── Authors.js&lt;br&gt;├── templates&lt;br&gt;│   └── authors&lt;br&gt;│       └── list.html&lt;br&gt;├── tests.py&lt;br&gt;├── urls.py&lt;br&gt;└── views.py&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The inheritor template in &lt;code&gt;authors/templates/authors/list.html&lt;/code&gt; &lt;strong&gt;extends&lt;/strong&gt; &lt;code&gt;core/templates/core/base.html&lt;/code&gt;, and then &lt;strong&gt;overrides&lt;/strong&gt; the Django &lt;code&gt;js&lt;/code&gt; block to declare its own import via &lt;strong&gt;import maps&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;{% extends &quot;core/base.html&quot; %}&lt;br&gt;{% load static %}&lt;br&gt;{% block content %}&lt;br&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;{% endblock %}&lt;br&gt;{% block js %}&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;importmap&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string-property property&quot;&gt;&quot;imports&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token string-property property&quot;&gt;&quot;strategy&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{% static &quot;&lt;/span&gt;authors&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;js&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Authors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;js&lt;span class=&quot;token string&quot;&gt;&quot; %}&quot;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;{{ block.super }}&lt;br&gt;{% endblock %}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now in &lt;code&gt;authors/static/authors/js/Authors.js&lt;/code&gt; we can export our strategy function:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;renderText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;p&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello import maps from the authors app!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; renderText &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The bit of HTML inside the &lt;code&gt;js&lt;/code&gt; block is the key:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;importmap&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string-property property&quot;&gt;&quot;imports&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token string-property property&quot;&gt;&quot;strategy&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{% static &quot;&lt;/span&gt;authors&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;js&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Authors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;js&lt;span class=&quot;token string&quot;&gt;&quot; %}&quot;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What happens now is that &lt;strong&gt;by loading this template via a Django view, the browser is smart enough to map the &lt;code&gt;strategy&lt;/code&gt; import to the actual JavaScript file &lt;code&gt;authors/static/authors/js/Authors.js&lt;/code&gt;&lt;/strong&gt; so that our code can import &lt;code&gt;renderText&lt;/code&gt; properly.&lt;/p&gt;
&lt;h3 id=&quot;the-books-app&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-import-maps/#the-books-app&quot;&gt;The books app&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;books&lt;/code&gt; app is one of the apps using the base template from &lt;code&gt;core&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The folder structure of the &lt;code&gt;books&lt;/code&gt; app:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;books&lt;br&gt;├── admin.py&lt;br&gt;├── apps.py&lt;br&gt;├── __init__.py&lt;br&gt;├── migrations&lt;br&gt;│   ├── __init__.py&lt;br&gt;├── models.py&lt;br&gt;├── static&lt;br&gt;│   └── books&lt;br&gt;│       └── js&lt;br&gt;│           └── Books.js&lt;br&gt;├── templates&lt;br&gt;│   └── books&lt;br&gt;│       └── list.html&lt;br&gt;├── tests.py&lt;br&gt;├── urls.py&lt;br&gt;└── views.py&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using the same mechanism, the inheritor template in &lt;code&gt;books/templates/books/list.html&lt;/code&gt; &lt;strong&gt;extends&lt;/strong&gt; &lt;code&gt;core/templates/core/base.html&lt;/code&gt;, and then &lt;strong&gt;overrides&lt;/strong&gt; the Django &lt;code&gt;js&lt;/code&gt; block to declare its own import via &lt;strong&gt;import maps&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;{% extends &quot;core/base.html&quot; %}&lt;br&gt;{% load static %}&lt;br&gt;{% block content %}&lt;br&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;{% endblock %}&lt;br&gt;{% block js %}&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;importmap&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string-property property&quot;&gt;&quot;imports&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token string-property property&quot;&gt;&quot;strategy&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{% static &quot;&lt;/span&gt;books&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;js&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Books&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;js&lt;span class=&quot;token string&quot;&gt;&quot; %}&quot;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;{{ block.super }}&lt;br&gt;{% endblock %}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the file &lt;code&gt;books/static/books/js/Books.js&lt;/code&gt; we can export our strategy function:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;renderText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;p&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello import maps from the books app!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; renderText &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, let&#39;s take a look at the import map inside the &lt;code&gt;js&lt;/code&gt; block:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;importmap&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string-property property&quot;&gt;&quot;imports&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token string-property property&quot;&gt;&quot;strategy&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{% static &quot;&lt;/span&gt;books&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;js&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Books&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;js&lt;span class=&quot;token string&quot;&gt;&quot; %}&quot;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, the browser here will map &lt;code&gt;strategy&lt;/code&gt; to the actual JavaScript file &lt;code&gt;books/static/books/js/Books.js&lt;/code&gt;, so that our code can import &lt;code&gt;renderText&lt;/code&gt; properly from this file.&lt;/p&gt;
&lt;p&gt;This simple technique is an implementation of the strategy pattern: &lt;strong&gt;the theory here is that we can dynamically point our JavaScript import to a different location, depending on the Django template&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-import-maps/#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By combining Django templates, JavaScript import maps, and a bit of creativity, we can implement the strategy pattern for our JavaScript frontend code.&lt;/p&gt;
&lt;p&gt;This technique can lead to &lt;strong&gt;high code reusability, and most important, modularity, two important traits of any healthy codebase&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;import maps are &lt;a href=&quot;https://web.dev/import-maps-in-all-modern-browsers/&quot;&gt;available in all modern browsers&lt;/a&gt;, so you can start using them today!&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;h2 id=&quot;suggested-resources&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django-import-maps/#suggested-resources&quot;&gt;Suggested resources&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/WICG/import-maps&quot;&gt;JavaScript import maps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.valentinog.com/blog/strategy-pattern-javascript/&quot;&gt;Avoiding death by thousands ifs with the strategy pattern&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Avoiding death by thousands ifs with the strategy pattern</title>
    <link href="https://www.valentinog.com/blog/strategy-pattern-javascript/" />
    <updated>2023-03-04T13:36:23Z</updated>
    <id>https://www.valentinog.com/blog/strategy-pattern-javascript/</id>
    <content type="html">&lt;p&gt;Most of the time, in JavaScript and TypeScript codebases structured as procedural code, &lt;strong&gt;design patterns are often neglected&lt;/strong&gt; in favor of spaghetti code which more often than not leads to an &lt;strong&gt;unmaintainable mess&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In this brief post we will see how to apply the strategy pattern to your frontend code, so you can forget those nasty ifs, and sleep at night.&lt;/p&gt;
&lt;h2 id=&quot;what-is-the-strategy-pattern-and-when-it-is-useful%3F&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/strategy-pattern-javascript/#what-is-the-strategy-pattern-and-when-it-is-useful%3F&quot;&gt;What is the strategy pattern and when it is useful?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Strategy_pattern&quot;&gt;strategy pattern&lt;/a&gt; is a software design pattern which lets you swap an algorithm at runtime.&lt;/p&gt;
&lt;p&gt;For example, one common application for the strategy pattern &lt;strong&gt;is a piece of code which needs to behave differently depending on the context where it&#39;s executed&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;To keep things practical, let&#39;s say you have a &lt;code&gt;UserRegistry&lt;/code&gt; which can &lt;code&gt;read&lt;/code&gt; and &lt;code&gt;persist&lt;/code&gt; &lt;strong&gt;users&lt;/strong&gt; to a &lt;strong&gt;storage&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The problem is that you don&#39;t know beforehand &lt;strong&gt;what the storage is and where it will persist the data&lt;/strong&gt;. In the context of a frontend application, we can imagine that at some point the storage could be &lt;code&gt;localStorage&lt;/code&gt; or even &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API&quot;&gt;IndexedDB&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;UserRegistry&lt;/code&gt; should not care what the actual implementation of the storage is, as long as it conforms to a particular interface which exposes save and read methods.&lt;/p&gt;
&lt;p&gt;Without the strategy pattern, the calling code might do something like:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;if storage === &quot;localStorage&quot; { &lt;br&gt;    // save and read logic specific for localStorage&lt;br&gt;}&lt;br&gt;&lt;br&gt;else if storage === &quot;IndexedDB&quot; {&lt;br&gt;   // save and read logic specific for IndexedDB&lt;br&gt;}&lt;br&gt;&lt;br&gt;else if storage === &quot;anotherStorage&quot; {&lt;br&gt;   // save and read logic specific for anotherStorage&lt;br&gt;}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without going too far, you can easily guess what this code can become.&lt;/p&gt;
&lt;p&gt;Personally, &lt;strong&gt;I&#39;m ok with &lt;code&gt;if&lt;/code&gt; statements at the tactical level, but not at the strategic level&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tactical level&lt;/strong&gt;: a function doing a small computation, or checking if some input is present, or if it&#39;s of the expected shape. In this case it&#39;s ok to have a couple of &lt;code&gt;if&lt;/code&gt; or an &lt;code&gt;if&lt;/code&gt; with an early return.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Strategic level&lt;/strong&gt;: a piece of code needs to behave differently depending on where it&#39;s called. In this case &lt;strong&gt;it&#39;s not feasible to handle conditions with&lt;/strong&gt; an &lt;code&gt;if&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using &lt;code&gt;if&lt;/code&gt; statements at the strategic level can lead to an &lt;strong&gt;explosion of conditional logic&lt;/strong&gt;. This is when the strategy pattern comes in.&lt;/p&gt;
&lt;h2 id=&quot;avoiding-death-by-thousands-ifs-with-the-strategy-pattern&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/strategy-pattern-javascript/#avoiding-death-by-thousands-ifs-with-the-strategy-pattern&quot;&gt;Avoiding death by thousands ifs with the strategy pattern&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here&#39;s a simple example of the strategy pattern in TypeScript. I&#39;m not a fan of JavaScript classes, and whenever possible I use objects as follows.&lt;/p&gt;
&lt;p&gt;First off, we define an &lt;a href=&quot;https://www.valentinog.com/blog/typescript/#typescript-tutorial-for-beginners-typescript-objects-and-interfaces&quot;&gt;interface&lt;/a&gt; for a generic &lt;strong&gt;storage strategy&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StorageStrategy&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;TItem&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TItem &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TItem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is an interface that concrete implementations can conform to.&lt;/p&gt;
&lt;p&gt;Next up, we use the interface for a more specific strategy which, let&#39;s imagine, talks to &lt;code&gt;localStorage&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; LocalStorageStrategy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StorageStrategy&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Person&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// Get from local storage&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Person&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// Save to localstorage&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s now define an &lt;strong&gt;interface for our program&lt;/strong&gt;, an imaginary registry to save and read entities from a storage:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Registry&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;TItem&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    storage&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StorageStrategy&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;TItem&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StorageStrategy&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;TItem&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TItem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TItem &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Objects conforming to the &lt;code&gt;Registry&lt;/code&gt; interface can be initialized with a &lt;code&gt;StorageStrategy&lt;/code&gt;. The registry does not care what the concrete implementation of the strategy is, as long as it conforms to the &lt;code&gt;StorageStrategy&lt;/code&gt; interface.&lt;/p&gt;
&lt;p&gt;Here&#39;s the concrete implementation of our program:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; UserRegistry&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Registry&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Person&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    storage&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strategy&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; strategy&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storage&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storage&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, this is how we can use our &lt;code&gt;UserRegistry&lt;/code&gt;. Here&#39;s the initialization where we pass the concrete implementation of the storage:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;UserRegistry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;LocalStorageStrategy&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How is this better than the &lt;code&gt;if&lt;/code&gt; version? Imagine that &lt;code&gt;UserRegistry&lt;/code&gt; is used in multiple pages of you application, and &lt;strong&gt;depending on where it&#39;s used, it should use a different type of storage&lt;/strong&gt;. In a page you&#39;ll do the following:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;UserRegistry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;LocalStorageStrategy&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In another page instead you&#39;ll do the following:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;UserRegistry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;IndexedDBStrategy&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s also not forget that with this arrangement, &lt;strong&gt;you can change the underlying algorithm at runtime&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This is way better than:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;if storage === &quot;localStorage&quot; { &lt;br&gt;    // save and read logic specific for localStorage&lt;br&gt;}&lt;br&gt;&lt;br&gt;else if storage === &quot;IndexedDB&quot; {&lt;br&gt;   // save and read logic specific for IndexedDB&lt;br&gt;}&lt;br&gt;&lt;br&gt;else if storage === &quot;anotherStorage&quot; {&lt;br&gt;   // save and read logic specific for anotherStorage&lt;br&gt;}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also, here&#39;s how we read data from the storage:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;UserRegistry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;15&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&#39;s instead how we persist data to the storage:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;UserRegistry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;12&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Blaise&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  age&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In both cases, &lt;code&gt;read&lt;/code&gt; and &lt;code&gt;persist&lt;/code&gt; interact with the concrete implementation of the storage.&lt;/p&gt;
&lt;p&gt;This can be easily adapted to plain JavaScript if you have the need to do so.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;h2 id=&quot;suggested-resources&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/strategy-pattern-javascript/#suggested-resources&quot;&gt;Suggested resources&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Practical Python Design Patterns by Wessel Badenhorst&lt;/li&gt;
&lt;li&gt;Design Patterns: Elements of Reusable Object-Oriented Software&lt;/li&gt;
&lt;li&gt;Fluent Python by Luciano Ramalho shows how to use the strategy pattern with functions in chapter 10&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Single-pages without the single-page with django2-tables, django-filter, and htmx</title>
    <link href="https://www.valentinog.com/blog/django2-tables-htmx/" />
    <updated>2023-02-12T17:42:06Z</updated>
    <id>https://www.valentinog.com/blog/django2-tables-htmx/</id>
    <content type="html">&lt;h2 id=&quot;introduction&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django2-tables-htmx/#introduction&quot;&gt;Introduction&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;ve been meaning to use &lt;a href=&quot;https://htmx.org/&quot;&gt;htmx&lt;/a&gt; since it came out, but I&#39;ve never had time, nor the occasion. Now the opportunity finally came to refactor an old Django view which uses the &lt;a href=&quot;https://datatables.net/&quot;&gt;Datatable jQuery plugin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I wanted to try something fresh, and &lt;strong&gt;htmx&lt;/strong&gt; seemed the way to go, paired with a couple of great libraries: &lt;strong&gt;django2-tables&lt;/strong&gt; and  &lt;strong&gt;django-filter&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Let&#39;s see how they play well together!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Please, take this post as personal notes, don&#39;t expect a step-by-step tutorial :-)&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-theory&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django2-tables-htmx/#the-theory&quot;&gt;The theory&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;htmx is a JavaScript library for building dynamic user interfaces which lets you enrich HTML elements with &amp;quot;magic&amp;quot; attributes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;With htmx, any actionable HTML element can make &lt;code&gt;XHR&lt;/code&gt; requests. The magic comes from two htmx attributes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hx-get&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hx-target&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;hx-get&lt;/code&gt; basically says: when the user clicks this element, make a &lt;code&gt;GET&lt;/code&gt; request to the given URL, then &lt;strong&gt;swap&lt;/strong&gt; the content of &lt;code&gt;hx-target&lt;/code&gt; with the partial response. (&lt;code&gt;POST&lt;/code&gt; requests are also supported).&lt;/p&gt;
&lt;p&gt;The basic principle behind htmx used in the context of server-side web frameworks is the following: &lt;strong&gt;if the frontend request comes from htmx, we return a partial HTML fragment&lt;/strong&gt; instead of the whole document.&lt;/p&gt;
&lt;p&gt;In Django, this translates to: &lt;strong&gt;if the request comes from htmx, we return a partial template&lt;/strong&gt; instead of the whole HTML template.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This approach can help give a single-page feeling to traditional web applications&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-recipe&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django2-tables-htmx/#the-recipe&quot;&gt;The recipe&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;My use case is a sortable/filterable table built with django2-tables and django-filter&lt;/strong&gt;. For this view I needed a single-page feeling, but zero time to build one!&lt;/p&gt;
&lt;p&gt;The building blocks used in this post are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Django&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jieter/django-tables2&quot;&gt;django2-tables&lt;/a&gt; which gives nice datatables out of the box&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/carltongibson/django-filter&quot;&gt;django-filter&lt;/a&gt; which adds filtering capabilities to Django views&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://htmx.org/&quot;&gt;htmx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/adamchainz/django-htmx&quot;&gt;django-htmx&lt;/a&gt;, a set of nice tools to integrate htmx into Django&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-view&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django2-tables-htmx/#the-view&quot;&gt;The view&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Django view I used in the project looks more or less like this:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProductList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;SingleTableMixin&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; FilterView&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    table_class &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ProductTable&lt;br&gt;    template_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;products/product_list.html&quot;&lt;/span&gt;&lt;br&gt;    model &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Product&lt;br&gt;    filterset_class &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ProductFilter&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;**&lt;/span&gt;kwargs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;htmx&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;            self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;template_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;products/htmx/table.html&quot;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;**&lt;/span&gt;kwargs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_queryset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Product&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;objects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This view uses &lt;code&gt;SingleTableMixin&lt;/code&gt; from django2-tables, and subclasses &lt;code&gt;FilterView&lt;/code&gt; from django-filters, as described in &lt;a href=&quot;https://django-tables2.readthedocs.io/en/latest/pages/filtering.html&quot;&gt;Filtering data in your table&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For the scope of this post I&#39;ll gloss over how &lt;code&gt;ProductFilter&lt;/code&gt; is configured: if you want to try things out, a simple configuration as described in the &lt;a href=&quot;https://django-filter.readthedocs.io/en/stable/guide/usage.html#the-filter&quot;&gt;django-filter tutorial will suffice&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What&#39;s worth noting in this view is the &lt;code&gt;get&lt;/code&gt; method: &lt;strong&gt;if the request comes from htmx, it swaps the Django template to return a partial&lt;/strong&gt; from &lt;code&gt;products/htmx/table.html&lt;/code&gt;, instead of the whole &lt;code&gt;products/product_list.html&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here I&#39;m also using &lt;a href=&quot;https://django-htmx.readthedocs.io/en/latest/middleware.html&quot;&gt;HtmxMiddleware&lt;/a&gt; from &lt;a href=&quot;https://github.com/adamchainz/django-htmx&quot;&gt;django-htmx&lt;/a&gt; which adds the &lt;code&gt;htmx&lt;/code&gt; object to each request. Very nice abstraction.&lt;/p&gt;
&lt;h2 id=&quot;the-templates&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django2-tables-htmx/#the-templates&quot;&gt;The templates&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s now take a look at the templates. To make this work we need two templates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;products/product_list.html&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;products/htmx/table.html&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first template, &lt;code&gt;products/product_list.html&lt;/code&gt;, simply includes the table partial:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;{% extends &quot;base.html&quot; %}&lt;br&gt;{% load static %}&lt;br&gt;{% load render_table from django_tables2 %}&lt;br&gt;&lt;br&gt;{% block content %}&lt;br&gt;    {% include &quot;products/htmx/table.html&quot; %}&lt;br&gt;{% endblock %}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second template instead, &lt;code&gt;products/htmx/table.html&lt;/code&gt;, is the partial:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;{% load render_table from django_tables2 %}&lt;br&gt;{% render_table table %}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the &lt;strong&gt;partial template&lt;/strong&gt; which gets &lt;strong&gt;returned from Django for any request coming from a htmx call&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Let&#39;s now see how all the pieces fall into place!&lt;/p&gt;
&lt;h2 id=&quot;single-page-like-sorting&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django2-tables-htmx/#single-page-like-sorting&quot;&gt;Single-page-like sorting&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;django2-tables adds links to each sortable column&lt;/strong&gt;. This is how a header cell looks like:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;th&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;orderable&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;?sort=user__name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        Name&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To get this under the control of htmx we need to add the two attributes you saw in the introduction:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hx-get&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hx-target&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, you can add these attributes to the table header cells rendered by &lt;strong&gt;django2-tables&lt;/strong&gt; by subclassing the default template &lt;code&gt;django_tables2/table.html&lt;/code&gt; in &lt;code&gt;products/htmx/table.html&lt;/code&gt;. This is how the template should look like:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;{% extends &quot;django_tables2/table.html&quot; %}&lt;br&gt;&lt;br&gt;{% load render_table from django_tables2 %}&lt;br&gt;{% load querystring from django_tables2 %}&lt;br&gt;&lt;br&gt;{% block table.thead %}&lt;br&gt;    {% if table.show_header %}&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;thead&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;table.attrs.thead.as_html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;            {% for column in table.columns %}&lt;br&gt;                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;th&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;column.attrs.th.as_html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;                    {% if column.orderable %}&lt;br&gt;                        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hx-get&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;                           &lt;span class=&quot;token attr-name&quot;&gt;hx-target&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;.table-container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;                           &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ column.header }} &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;                    {% else %}&lt;br&gt;                        {{ column.header }}&lt;br&gt;                    {% endif %}&lt;br&gt;                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;            {% endfor %}&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;thead&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    {% endif %}&lt;br&gt;{% endblock table.thead %}&lt;br&gt;{% render_table table %}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the most important part:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hx-get&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;   &lt;span class=&quot;token attr-name&quot;&gt;hx-target&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;.table-container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;   &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; {{ column.header }} &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These attributes instruct htmx to send a &lt;code&gt;GET&lt;/code&gt; request to Django, which will return the partial template, and to swap the content of the element &lt;code&gt;.table-container&lt;/code&gt; with the partial response.&lt;/p&gt;
&lt;h2 id=&quot;single-page-like-filtering&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django2-tables-htmx/#single-page-like-filtering&quot;&gt;Single-page-like filtering&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The principle to make &lt;strong&gt;django-filter work with htmx&lt;/strong&gt; is the same you already saw above, but this time we need to change &lt;code&gt;products/product_list.html&lt;/code&gt;, which is the container template where the filter form lives.&lt;/p&gt;
&lt;p&gt;To give the filter a single-page-like feeling, you can render &lt;strong&gt;django-filter form&lt;/strong&gt; as follows:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;{% extends &quot;base.html&quot; %}&lt;br&gt;{% load static %}&lt;br&gt;{% load render_table from django_tables2 %}&lt;br&gt;&lt;br&gt;{% block content %}&lt;br&gt;    {% if filter %}&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hx-get&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hx-target&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;.table-container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;            # Your django-filter fields here ...&lt;br&gt;&lt;br&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Apply filters&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    {% endif %}&lt;br&gt;&lt;br&gt;    {% include &quot;products/htmx/table.html&quot; %}&lt;br&gt;{% endblock %}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, you can see how the form is progressively enhanced with htmx:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hx-get&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hx-target&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;.table-container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, &lt;strong&gt;any filtered request coming from the form will be controlled by htmx&lt;/strong&gt;, which will swap the target container &lt;code&gt;.table-container&lt;/code&gt; with the partial response.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;code&gt;.table-container&lt;/code&gt; gets added out-of-the-box by django2-tables&lt;/p&gt;
&lt;h2 id=&quot;single-page-like-pagination&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django2-tables-htmx/#single-page-like-pagination&quot;&gt;Single-page-like pagination&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Coming soon&lt;/p&gt;
&lt;h2 id=&quot;is-htmx-any-good%3F&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django2-tables-htmx/#is-htmx-any-good%3F&quot;&gt;Is htmx any good?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I should admit that &lt;strong&gt;htmx is great for adding the single-page-like feeling&lt;/strong&gt; to Django views and I quite enjoy working with it. I should also add that &lt;strong&gt;at this stage I&#39;ve still not enough elements to judge its scalability and maintainability&lt;/strong&gt;, especially for more complex use cases. Time will tell!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: after using it on a bunch of projects, I&#39;m more and more convinced that htmx is a great tool for building MVPs, but it&#39;s also important to &lt;strong&gt;not take it to the extremes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading and stay tuned!&lt;/p&gt;
&lt;h2 id=&quot;links&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/django2-tables-htmx/#links&quot;&gt;Links&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jieter/django-tables2&quot;&gt;django2-tables&lt;/a&gt; which gives nice datatables out of the box&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/carltongibson/django-filter&quot;&gt;django-filter&lt;/a&gt; which adds filtering capabilities to Django views&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://htmx.org/&quot;&gt;htmx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/adamchainz/django-htmx&quot;&gt;django-htmx&lt;/a&gt;, a set of nice tools to integrate htmx into Django&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Today I re-learned: Python function default arguments are retained between executions</title>
    <link href="https://www.valentinog.com/blog/tirl-python-default-arguments/" />
    <updated>2023-01-18T16:01:51Z</updated>
    <id>https://www.valentinog.com/blog/tirl-python-default-arguments/</id>
    <content type="html">&lt;p&gt;This is the story of a tricky bug in a Python application I maintain that was pestering me.&lt;/p&gt;
&lt;p&gt;So, the problem is this. Given the following Python code, I expect the method &lt;code&gt;is_ongoing&lt;/code&gt; to return &lt;code&gt;False&lt;/code&gt; when &lt;code&gt;today&lt;/code&gt; is not included between &lt;code&gt;self.start_date&lt;/code&gt; and &lt;code&gt;self.end_date&lt;/code&gt;, and to return &lt;code&gt;True&lt;/code&gt; when &lt;code&gt;today&lt;/code&gt; is included between &lt;code&gt;self.start_date&lt;/code&gt; and &lt;code&gt;self.end_date&lt;/code&gt;. Simple as it is:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;is_ongoing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; today &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;today&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;today &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;start_date&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;today &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;end_date&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This method is used to respectively show or hide a piece of interface to users. But the &lt;strong&gt;code wasn&#39;t working as expected&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The comparison would produce &lt;code&gt;False&lt;/code&gt; if the user happened to trigger &lt;code&gt;is_ongoing&lt;/code&gt; in a day not included between &lt;code&gt;self.start_date&lt;/code&gt; and &lt;code&gt;self.end_date&lt;/code&gt;, and &lt;strong&gt;the function would continue to return&lt;/strong&gt; &lt;code&gt;False&lt;/code&gt; &lt;strong&gt;on each subsequent call&lt;/strong&gt;. Weird!!&lt;/p&gt;
&lt;h2 id=&quot;tests-say-more-than-a-thousand-words-...&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/tirl-python-default-arguments/#tests-say-more-than-a-thousand-words-...&quot;&gt;Tests say more than a thousand words ...&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I &lt;strong&gt;couldn&#39;t understand why an apparent simple comparison between two dates was not working as expected&lt;/strong&gt;, so having a bit of spare time I decided to put the code under test, since this is some &lt;strong&gt;code I inherited, and not everything there is covered by tests&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Here&#39;s the test I set up:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test_lesson_status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lesson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;start_date &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2023&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lesson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;end_date &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2023&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;27&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; time_machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;travel&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2023&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;assertFalse&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lesson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;is_ongoing&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; time_machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;travel&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2023&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;27&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;assertTrue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lesson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;is_ongoing&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To test the passage of time I used the excellent &lt;a href=&quot;https://github.com/adamchainz/time-machine&quot;&gt;time-machine&lt;/a&gt; by Adam Johnson, which by the way seems to be an &lt;a href=&quot;https://adamj.eu/tech/2021/02/19/freezegun-versus-time-machine/&quot;&gt;order of magnitude faster than another competing library, freezegun&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Turns out, by running the test, &lt;strong&gt;I surprisingly discovered that the second assertion was always failing&lt;/strong&gt;: a lesson starting 2023-1-25, ending 2023-1-27 is not ongoing if today is 2023-1-18, but it should be absolutely ongoing if &amp;quot;today&amp;quot; is 2023-1-27.&lt;/p&gt;
&lt;p&gt;At this point &lt;strong&gt;I quickly understood that someway, somehow, Python was &amp;quot;remembering&amp;quot; the initial state of the default argument&lt;/strong&gt; &lt;code&gt;today&lt;/code&gt;. Here&#39;s the function again:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;is_ongoing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; today &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;today&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;today &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;start_date&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;today &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;end_date&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Writing JavaScript almost every day, I took for granted that &lt;strong&gt;function default arguments in Python were recomputed on each call&lt;/strong&gt; like JavaScript engines do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Turns out instead that &lt;a href=&quot;https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments&quot;&gt;Python’s default arguments are evaluated once when the function is defined, not each time the function is called&lt;/a&gt;&lt;/strong&gt;. Also, relevant: &lt;a href=&quot;https://florimond.dev/en/posts/2018/08/python-mutable-defaults-are-the-source-of-all-evil/&quot;&gt;Python Mutable Defaults Are The Source of All Evil&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I guess the Python interpreter creates a closure over the function, effectively retaining its scope.&lt;/p&gt;
&lt;p&gt;The funny thing is that this code was written by Python developers more expert than me, nobody ever noticed this bug, and most important, it was an easy fix:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;is_ongoing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    today &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;today&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;today &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;start_date&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;today &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;end_date&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Moving &lt;code&gt;today&lt;/code&gt; inside the function fixed the thing: &lt;strong&gt;what I want is to recompute the date on every invocation&lt;/strong&gt;, and this function wasn&#39;t used anywhere else, thus there is no need for a default argument.&lt;/p&gt;
&lt;h2 id=&quot;key-takeaways&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://www.valentinog.com/blog/tirl-python-default-arguments/#key-takeaways&quot;&gt;Key takeaways&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Today I re-learned that Python function default arguments are retained between executions. I&#39;m sure I fell on this trick in the past, so better write a couple of words about it for my future self&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Know your tools&lt;/strong&gt;: I kind of know most of the machinery behind JavaScript engines, and I try to stay updated about it. &lt;strong&gt;Understanding why and how the interpreter of your main language works is important as writing good code&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Test, test, test&lt;/strong&gt;: I know this has been said a billion of times, but I cannot stress enough why &lt;strong&gt;testing even simple things is important&lt;/strong&gt;. Sometimes we neglect to test simple implementations because they appear so trivial most of the time, but it&#39;s there that silly bugs lie.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use a linter&lt;/strong&gt;: fellow developers on &lt;a href=&quot;https://www.reddit.com/r/Python/comments/10gt7tv/today_i_relearned_python_function_default/&quot;&gt;Reddit&lt;/a&gt; kindly pointed out that &lt;a href=&quot;https://github.com/PyCQA/flake8-bugbear&quot;&gt;flake8-bugbear&lt;/a&gt; can catch this type of &amp;quot;bugs&amp;quot;. The bugbear rule is B008, which reports the following:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;B008 Do not perform function calls in argument defaults.  The call is performed only once at function definition time. All calls to your function will reuse the result of that definition-time function call.  If this is intended, assign the function call to a module-level variable and use that variable as a default value.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</content>
  </entry>
</feed>