<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Linuxkurs-Blog (Posts about Django)</title><link>https://lannert.de/debloss/</link><description></description><atom:link href="https://lannert.de/debloss/en/categories/django.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2025 &lt;a href="mailto:lannert@hhu.de"&gt;Detlef Lannert&lt;/a&gt; 
&lt;a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/"&gt;
&lt;img alt="Creative Commons License BY-NC-SA"
style="border-width:0; margin-bottom:12px;"
src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png"&gt;&lt;/a&gt;</copyright><lastBuildDate>Fri, 12 Dec 2025 21:53:11 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Ein schnelles, typsicheres API-Framework für Python mit Django-ORM</title><link>https://lannert.de/debloss/en/posts/ein-schnelles-typsicheres-api-framework-fur-python-mit-django-orm/</link><dc:creator>Detlef Lannert</dc:creator><description>&lt;p&gt;&lt;a class="reference external" href="https://github.com/FarhanAliRaza/django-bolt"&gt;django-bolt&lt;/a&gt; ist ein auf
Rust aufbauendes API-Framework, schneller als FastAPI, das mit dem Django-ORM,
Django-Admin und Django-Paketen arbeitet und voll typisiert ist.&lt;/p&gt;</description><guid>https://lannert.de/debloss/en/posts/ein-schnelles-typsicheres-api-framework-fur-python-mit-django-orm/</guid><pubDate>Fri, 12 Dec 2025 19:00:00 GMT</pubDate></item><item><title>Spambots aussperren, ohne Menschen mit Bilderrätseln zu plagen</title><link>https://lannert.de/debloss/en/posts/spambots-aussperren-ohne-menschen-mit-bilderratseln-zu-plagen/</link><dc:creator>Detlef Lannert</dc:creator><description>&lt;p&gt;&lt;a class="reference external" href="https://mosparo.io/"&gt;mosparo&lt;/a&gt; ist ein Open-Source-Spamchecker für
Webformulare, der nicht das Lösen von Bilderrätseln verlangt und keine Daten
an Google schickt – das scheint ein guter Captcha-Ersatz zu sein.
Es gibt Plugins zur leichteren Integration in z.B. Django oder Typo3.
Die mosparo Association ist eine gemeinnützige Organisation in der Schweiz.&lt;/p&gt;</description><guid>https://lannert.de/debloss/en/posts/spambots-aussperren-ohne-menschen-mit-bilderratseln-zu-plagen/</guid><pubDate>Fri, 05 Dec 2025 19:00:00 GMT</pubDate></item><item><title>Welche DB-Features unterstützt der Django-ORM?</title><link>https://lannert.de/debloss/en/posts/welche-db-features-unterstuetzt-der-django-orm/</link><dc:creator>Detlef Lannert</dc:creator><description>&lt;p&gt;Ein relativ neuer Artikel stellt in
einer &lt;a class="reference external" href="https://www.paulox.net/2025/10/06/django-orm-comparison/"&gt;Übersicht&lt;/a&gt;
die Features des Django-ORM zusammen, die von den einzelnen Datenbank-Backends
(PostgreSQL, SQLite, MariaDB, MySQL, Oracle) unterstützt werden.&lt;/p&gt;</description><guid>https://lannert.de/debloss/en/posts/welche-db-features-unterstuetzt-der-django-orm/</guid><pubDate>Sun, 09 Nov 2025 19:00:00 GMT</pubDate></item><item><title>Feature flags nutzen</title><link>https://lannert.de/debloss/en/posts/feature-flags-nutzen/</link><dc:creator>Detlef Lannert</dc:creator><description>&lt;p&gt;&lt;a class="reference external" href="https://rtpg.co/2023/11/15/feature-flags-in-depth/"&gt;Betrachtungen&lt;/a&gt; zum
Arbeiten mit Feature flags beim Programmieren mit Python:
Warum sie nützlich sind, wie man sie leicht einrichten kann und wie man sie
wieder los wird. [Via zerotomastery.io]&lt;/p&gt;</description><guid>https://lannert.de/debloss/en/posts/feature-flags-nutzen/</guid><pubDate>Mon, 29 Sep 2025 20:22:22 GMT</pubDate></item><item><title>django-watchfiles: Effizienteres Erkennen geänderter Module</title><link>https://lannert.de/debloss/en/posts/django-watchfiles-effizienteres-erkennen-geanderter-module/</link><dc:creator>Detlef Lannert</dc:creator><description>&lt;p&gt;Der Django-Testserver, der meist mit &lt;code class="docutils literal"&gt;manage.py runserver&lt;/code&gt; (oder &lt;code class="docutils literal"&gt;_plus&lt;/code&gt;)
gestartet wird, lädt sich neu, wenn Quelldateien geändert werden. Die Erkennung
solcher Änderungen ist ziemlich ineffizient, weil offenbar immer wieder im
Dateisystem nachgeschaut wird, ob noch alles beim alten geblieben ist.&lt;/p&gt;
&lt;p&gt;Das Paket django-watchfiles ändert dies so ab, dass die Mechanismen des
Betriebssystems genutzt werden, um Informationen über Änderungen zu bekommen.
Dies führt zu einer deutlichen CPU-Entlastung, wie
&lt;a class="reference external" href="https://adamj.eu/tech/2025/09/22/introducing-django-watchfiles/"&gt;der Autor des Pakets berichtet&lt;/a&gt;.&lt;/p&gt;</description><guid>https://lannert.de/debloss/en/posts/django-watchfiles-effizienteres-erkennen-geanderter-module/</guid><pubDate>Mon, 22 Sep 2025 09:00:00 GMT</pubDate></item><item><title>In Django mit Materialized Views arbeiten</title><link>https://lannert.de/debloss/en/posts/in-django-mit-materialized-views-arbeiten/</link><dc:creator>Detlef Lannert</dc:creator><description>&lt;p&gt;Auch in einer Django-Anwendung kann man mit PostgreSQLs Materialized Views arbeiten –
&lt;a class="reference external" href="https://pganalyze.com/blog/postgresql-views-django-python"&gt;dieser Artikel&lt;/a&gt; erklärt, was man wie
machen muss. Nämlich&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;ein passendes Model definieren, das unmanaged ist und den View als Table hat&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;den View innerhalb einer Migration erzeugen (&lt;code class="docutils literal"&gt;CREATE MATERIALIZED VIEW name AS …&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;den View gelegentlich (concurrently) refreshen (evtl. könnte man sich im Cache merken, ob er
aktuell ist, und per Signal diesen Merker löschen, wenn sich an den verwendeten Tabellen etwas
ändert, etwa durch Speichern oder Löschen von Objekten)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;beachten, dass der View eine „unique“ indizierbare Spalte hat (sonst geht concurrent refresh nicht)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Abfragen solcher Views können wesentlich schneller sein als bei konventionellen Views, deren Daten
bei jedem Zugriff wieder „on the fly“ erstellt werden müssen.
Mehr dazu findet sich &lt;a class="reference external" href="https://www.postgresql.org/docs/current/rules-materializedviews.html"&gt;in der PostgreSQL-Doku&lt;/a&gt;.&lt;/p&gt;</description><guid>https://lannert.de/debloss/en/posts/in-django-mit-materialized-views-arbeiten/</guid><pubDate>Thu, 11 Jul 2024 08:00:00 GMT</pubDate></item><item><title>Tabellenübergreifende Constraints in Django realisieren</title><link>https://lannert.de/debloss/en/posts/tabellenubergreifende-constraints-in-django-realisieren/</link><dc:creator>Detlef Lannert</dc:creator><description>&lt;p&gt;Django-Datenbank-Constraints können keine Felder außerhalb der jeweiligen Tabelle referenzieren und
damit z. B. Foreign-Key-Beziehungen plausibilisieren.
Die &lt;a class="reference external" href="https://www.postgresql.org/docs/current/ddl-constraints.html"&gt;PostgreSQL-Doku&lt;/a&gt;
erklärt, was DB-seitig möglich ist und warum keine anderen Rows oder Tables angesprochen werden dürfen
(bzw. sollen). Um bestimmte übergreifende Restriktionen beim Einfügen einer Zeile abzuprüfen, sollten
demzufolge Trigger verwendet werden.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://bsago.me/tech-notes/check-nested-relations-with-postgresql-triggers"&gt;Dieser Artikel&lt;/a&gt; gibt ein
schönes Beispiel für einen Trigger (in plpgsql), der Werte einer neu eingefügten Row gegen vorhandene
Einträge in anderen Tabellen prüft und ggf. ein Insert (oder auch ein Update) verhindert.&lt;/p&gt;
&lt;p&gt;Wie man den Trigger (und ähnliches) per Django-Migration in die Datenbank bekommt, wird in der
&lt;a class="reference external" href="https://stackoverflow.com/questions/68381971/how-to-use-postgresqls-stored-procedures-or-functions-in-django-project"&gt;Antwort auf diese Stackexchange-Frage&lt;/a&gt;
mit einem einfachen Beispiel erläutert.&lt;/p&gt;
&lt;p&gt;Allerdings muss man in den Views vorsichtig sein:
Wenn bei einem Insert oder Update über den Django-ORM so ein Trigger mit einer Exception zuschlägt,
bekommt man einen IntegrityError, der dann auch abgefangen werden sollte. Sonst kommt es zum Error500.
Die Ursache für den Error ist nicht so ganz einfach zu erkennen – deshalb sollte man durch
Vorababfragen die Situation möglichst vermeiden (und dann ordentlich anmeckern).&lt;/p&gt;
&lt;p&gt;Der Trigger ist dann trotzdem nicht überflüssig – er verhindert
vor allem, dass durch Umgehung der entsprechenden Views oder durch Race conditions die Prüfung
das Eintragen falscher Daten nicht wirksam verhindert.&lt;/p&gt;</description><guid>https://lannert.de/debloss/en/posts/tabellenubergreifende-constraints-in-django-realisieren/</guid><pubDate>Wed, 10 Jul 2024 14:18:16 GMT</pubDate></item><item><title>Django-ORM ohne das Django-Framework benutzen</title><link>https://lannert.de/debloss/en/posts/django-orm-ohne-framework/</link><dc:creator>Detlef Lannert</dc:creator><description>&lt;p&gt;Der Django-ORM kann auch nützlich sein für Anwendungen, die zwar mit einer Datenbank, aber nicht mit
einem Web-Framework arbeiten sollen, und manchmal möchte man auch ein einem Programm mit einer von
Django verwalteten Datenbank arbeiten, ohne die gesamte Django-Umgebung zu aktivieren (und vielleicht
kommt die Lösung, das Programm als Management-Kommando laufen zu lassen, aus irgendwelchen Gründen
nicht in Frage).&lt;/p&gt;
&lt;p&gt;Es ist durchaus möglich, lediglich den ORM zu aktivieren, ohne URLs, Views, komplette Settings usw.
einzurichten. Ein &lt;a class="reference external" href="https://abdus.dev/posts/django-orm-standalone/"&gt;Blogpost&lt;/a&gt; zeigt eine einfache
Möglichkeit auf, in wenigen Zeilen alles einzurichten, was für den ORM benötigt wird:&lt;/p&gt;
&lt;pre class="literal-block"&gt;#!/usr/bin/env python3

def init_django():
    import django
    from django.conf import settings

    if not settings.configured:
        settings.configure(
            INSTALLED_APPS=["db"],
            DATABASES={
                "default": {
                    "ENGINE": "django.db.backends.postgresql", "NAME": …
                    }
                }
            )
        django.setup()


if __name__ == "__main__":
    from django.core.management import execute_from_command_line

    init_django()
    execute_from_command_line()&lt;/pre&gt;
&lt;p&gt;Im Modul db/models.py stehen dann die Klassendefinitionen für die Models:&lt;/p&gt;
&lt;pre class="literal-block"&gt;from django.db import models
from manage import init_django  # oder ggf. "from ..manage …"

init_django()

class MyModel(models.Model):
    …&lt;/pre&gt;
&lt;p&gt;&lt;a class="reference external" href="https://forum.djangoproject.com/t/django-orm-as-a-standalone-library/4971"&gt;Es geht noch kürzer&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;import os
import django

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
django.setup()&lt;/pre&gt;
&lt;p&gt;In dieser Variante stehen die Settings in einem eigenen Modul &lt;code class="docutils literal"&gt;project/settings.py&lt;/code&gt;, brauchen
aber natürlich auch nur das Allernötigste zu umfassen (siehe oben: &lt;code class="docutils literal"&gt;INSTALLED_APPS&lt;/code&gt; und
&lt;code class="docutils literal"&gt;DATABASES&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Wie man's auch macht – man hat dann eine Anwendung, die man leicht nachträglich zu einer vollständigen
Web-App aufbohren kann.&lt;/p&gt;</description><guid>https://lannert.de/debloss/en/posts/django-orm-ohne-framework/</guid><pubDate>Mon, 08 Jul 2024 08:00:00 GMT</pubDate></item><item><title>Django views the right way</title><link>https://lannert.de/debloss/en/posts/django-views-the-right-way/</link><dc:creator>Detlef Lannert</dc:creator><description>&lt;p&gt;Der Artikel
&lt;a class="reference external" href="https://spookylukey.github.io/django-views-the-right-way/"&gt;Django views the right way&lt;/a&gt;
von Luke Plant enthält eine ganze Reihe von &lt;em&gt;best practice recommendations&lt;/em&gt; für Django-Views.&lt;/p&gt;
&lt;p&gt;Einige – mir interessant erscheinende – Punkte seien herausgegriffen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;TemplateResponse()&lt;/code&gt; statt &lt;code class="docutils literal"&gt;render()&lt;/code&gt; zu verwenden hat einige Vorteile&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;FBV statt CBV benutzen – er nennt viele Argumente dafür und weist darauf hin, dass
die Einsparung von Boilerplate-Code vor allem die Lesbarkeit verbessert, mehr noch als das
Schreiben zu erleichtern&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/AliSayyah/django-urlconfchecks/"&gt;django-urlconfchecks&lt;/a&gt; kann prüfen, ob
die urls.py-Einträge zu den Views passen bzgl. Anzahl und Typen der View-Parameter
(die statischen Type-Checker können das nicht)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Das request-Objekt eines Views sollte nicht herumgereicht werden, vor allem nicht zu den models.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Das Objekt eines Detail-Views im context sollte nicht &lt;code class="docutils literal"&gt;object&lt;/code&gt; heißen
(und demzufolge das Objekt eines List-Views nicht &lt;code class="docutils literal"&gt;object_list&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Die Paginierung längerer Ausgaben kann man standardisieren:&lt;/p&gt;
&lt;pre class="literal-block"&gt;context = { …
        } | paged_object_list_context(request, products, paginate_by=5)&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sehr dynamische Formulare sollte man nicht als Form definieren, sondern zu Fuß definieren.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Precondition checks mit Decorators sind eine gute Idee – einige nützliche Beispiele werden
dafür gegeben.
Die Nutzung von &lt;code class="docutils literal"&gt;@functools.wraps(view_func)&lt;/code&gt; ist ratsam!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mehrere Decorators eines FBV kombinieren sich gut, mehrere Mixins eines CBV aber eher nicht!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Gute Hinweise, wie man sicherheitsrelevante Decorators in einer App erzwingen kann&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wie kann man Views „dünn“ halten?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Benutzerbezogene Queries sollte man über das Benutzerobjekt laufen lassen: &lt;code class="docutils literal"&gt;user.bookings.in_basket()&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ein Service-Layer wird hier abgelehnt.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optimierungen (wie &lt;code class="docutils literal"&gt;select_related&lt;/code&gt; &amp;amp; Co.) lassen sich nur schwerlich aus dem Views-Layer
heraushalten, eine ordentliche &lt;em&gt;separation of concerns&lt;/em&gt; ist praktisch nicht möglich.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;</description><guid>https://lannert.de/debloss/en/posts/django-views-the-right-way/</guid><pubDate>Sun, 07 Jul 2024 08:00:00 GMT</pubDate></item><item><title>Choices mit Constraints überwachen</title><link>https://lannert.de/debloss/en/posts/choices-mit-constraints-uberwachen/</link><dc:creator>Detlef Lannert</dc:creator><description>&lt;p&gt;Django-Constraints können auf einfache Weise
&lt;a class="reference external" href="https://adamj.eu/tech/2020/01/22/djangos-field-choices-dont-constrain-your-data/"&gt;genutzt werden,&lt;/a&gt;
um Choices durchzusetzen:&lt;/p&gt;
&lt;pre class="literal-block"&gt;check=models.Q(status__in=Status.values), …&lt;/pre&gt;</description><guid>https://lannert.de/debloss/en/posts/choices-mit-constraints-uberwachen/</guid><pubDate>Mon, 01 Jul 2024 08:00:00 GMT</pubDate></item></channel></rss>