<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2germanfull.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" version="2.0"><channel><title>freakydog</title><link>http://www.freakydog.de/</link><description>Die letzten 30 Einträge</description><language>de</language><lastBuildDate>Thu, 09 Apr 2009 10:44:18 -0000</lastBuildDate><geo:lat>54.417</geo:lat><geo:long>13.433</geo:long><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/freakydog" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item><title>Überschreiben hochgeladener Dateien</title><link>http://www.freakydog.de/uberschreiben-hochgeladener-dateien.html</link><description>&lt;p&gt;Wird eine Datei für ein FileField oder ImageField hochgeladen und exisitiert bereits, überschreibt Django diese Datei nicht sondern hängt einen Tiefstrich an die Datei. Die ältere Datei bleibt ohne Verweis im Dateisystem bestehen.&lt;/p&gt;
&lt;p&gt;Überschreibst du also eine Datei &lt;code&gt;rechnung.pdf&lt;/code&gt; drei mal, entsteht eine lustige Kaskade im Dateisystem.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rechnung.pdf&lt;/li&gt;
&lt;li&gt;rechnung_.pdf&lt;/li&gt;
&lt;li&gt;rechnung__.pdf&lt;/li&gt;
&lt;li&gt;rechnung___.pdf (Der Pfad zu dieser Datei wird im FileField gespeichert)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Es ist nicht immer im Interesse, diese alten Dateien zu behalten. Sei es aus Platzmangel, Datenschutzgründen oder weil man einfach nur Ordnung auf seinem System halten will.&lt;/p&gt;
&lt;p&gt;Derzeit gibt es keinen direkten Weg, das überschreiben zu erzwingen. Es gibt schon seit längerer Zeit ein &lt;a href="http://code.djangoproject.com/ticket/4339"&gt;Ticket&lt;/a&gt; im Django-Repository aber es schaut nicht so aus, als ob es in Django 1.1 eingespielt wird.&lt;/p&gt;
&lt;h3&gt;Automatisches Übeschreiben mit einem Custom-Storage ###&lt;/h3&gt;
&lt;p&gt;Hochgeladene Dateien werden von einem eigenen Modul, dem &lt;code&gt;FileSystemStorage&lt;/code&gt; verarbeitet. Es öffnet und speichert die Dateien und prüft, ob der Dateiname schon exisitiert und hängt ggf. einen Tiefstrich an die Datei. Das passiert in der Methode &lt;code&gt;get_available_name&lt;/code&gt;. Genau diese ist der Ansatzpunkt um das Überschreiben zu erzwingen. &lt;/p&gt;
&lt;p&gt;Hier wird ein eigenes Storage erstellt aber vor dem Speichern der Datei, wird (wenn sie existiert) die alte Datei gelöscht:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.files.storage&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FileSystemStorage&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OverwriteFileSystemStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FileSystemStorage&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_available_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;invoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;upload_invoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;OverwriteFileSystemStorage&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__unicode__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Das Custom-Storage wird dem &lt;code&gt;FileField&lt;/code&gt; oder dem &lt;code&gt;ImageField&lt;/code&gt; als Attribut &lt;code&gt;storage&lt;/code&gt; übergeben. Soll dieses Schema für &lt;em&gt;alle&lt;/em&gt; Uploads gelten, ändere in der &lt;code&gt;settings.py&lt;/code&gt; den Wert &lt;code&gt;DEFAULT_FILE_STORAGE&lt;/code&gt; Standardmässig wird das oben genannte &lt;code&gt;FileSystemStorage&lt;/code&gt; verwendet.&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;# Original&lt;/span&gt;
&lt;span class="n"&gt;DEFAULT_FILE_STORAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;django.core.files.storage.FileSystemStorage&amp;#39;&lt;/span&gt;

&lt;span class="c"&gt;# Immer alle Dateien überschreiben&lt;/span&gt;
&lt;span class="n"&gt;DEFAULT_FILE_STORAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;path.to.OverwriteFileSystemStorage&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;h3&gt;Den originalen Dateinamen zusätzlich speichern&lt;/h3&gt;
&lt;p&gt;Dass Dateien nicht grundsätzlich überschrieben werden ist ja grundsätzlich keine schlechte Idee. Schließlich hat man immer ein automatisches Backup aller Änderungen. Lassen wir das Schema so wie es ist aber speichern wir den originalen Dateinamen zusätzlich in einem eigenen Feld. So können wir dem User später den orignalen Dateinamen anzeigen, im Dateisystem bleiben aber alle überschriebenen Dateien erhalten.&lt;/p&gt;
&lt;p&gt;Ein upload-Callable ist ein schneller und sicherer Weg, dies zu erreichen. Mehr dazu hatte ich schon in &lt;a href="http://www.mahner.org/weblog/ordnung-im-medienordner-dynamische-upload-pfade/"&gt;Ordnung im Medienordner&lt;/a&gt; geschrieben.&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload_invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoice_orig_filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;

    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;invoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;upload_invoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;OverwriteFileSystemStorage&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;invoice_orig_filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blank&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__unicode__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;h3&gt;Dateien löschen&lt;/h3&gt;
&lt;p style="float: right; margin-right: -200px; margin-left: 1em;"&gt;&lt;img src="http://static.mahner.org/assets/weblog/django/deletewidget.png" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;Der Vollständigkeit halber: Von Haus aus ist es im Admin noch nicht möglich, hochgeladene Dateien wieder zu löschen. &lt;a href="http://www.sjaekel.com/"&gt;Stephan Jäkel&lt;/a&gt; hat ein &lt;a href="http://www.sjaekel.com/blog/2008/11/6/22-adminfilewidget-mit-delete-funktion/"&gt;Admin-Widget&lt;/a&gt; konstruiert, dass Dateien auf Klick wieder löscht.&lt;/p&gt;</description><pubDate>Thu, 09 Apr 2009 10:44:18 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/uberschreiben-hochgeladener-dateien.html</guid></item><item><title>Group By Funktionen in Querysets (Django 1.1+)</title><link>http://www.freakydog.de/group-funktionen-querysets-django-11.html</link><description>&lt;p&gt;In meinem aktuellen Projekt will ich die Anzahl neuer Einträge in einem Model pro Tag in einer Grafik darstellen, ich habe also ein Problem wie:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Gib mir eine Liste aller Tage an denen Beiträge hinzugefügt wurden und wie viele Beiträge waren es an diesem Tag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ein SQL-Query dafür ist einfach:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;entry_table&lt;/span&gt; &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Nun schauen wir mal, wie sich das mit Django 1.1 Bordmitteln lösen lässt. Wir erstellen ein Django-Model:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Und füttern es gleich mit ein paar Testdaten:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;2009-03-25&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Irgendein Titel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;2009-03-24&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Irgendein Titel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;2009-03-24&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Irgendein Titel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Während man in Django 1.0 noch rohes SQL walten lassen musste, wurden Django 1.1 (aktuell noch &lt;em&gt;django-trunk&lt;/em&gt;) einige &lt;a href="http://docs.djangoproject.com/en/dev/ref/models/querysets/#id8"&gt;Aggreagations-Funktionen&lt;/a&gt; hinzugefügt, um übliche Aufgaben wie Count, Average, Max und Min etc. direkt im Queryset durchführen zu können. Für unseren Fall brauchen wir die &lt;a href="http://docs.djangoproject.com/en/dev/ref/models/querysets/#id9"&gt;Count&lt;/a&gt;-Funktion.&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Count&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;created&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="go"&gt;[{&amp;#39;counter&amp;#39;: 2, &amp;#39;created&amp;#39;: datetime.date(2009, 3, 24)},&lt;/span&gt;
&lt;span class="go"&gt; {&amp;#39;counter&amp;#39;: 1, &amp;#39;created&amp;#39;: datetime.date(2009, 3, 25)}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;&lt;code&gt;values('created')&lt;/code&gt; triggert hierbei das "GROUP BY created" und &lt;code&gt;Count('id')&lt;/code&gt; ist das Pendant zur SQL-Funktion &lt;code&gt;COUNT(id)&lt;/code&gt;. &lt;code&gt;annotate&lt;/code&gt; &lt;em&gt;fügt&lt;/em&gt; diese Daten dem SELECT hinzu.&lt;/p&gt;
&lt;p&gt;Super einfach, oder? &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :)" title=" :)" class="smiley"/&gt;&lt;/p&gt;
&lt;h3&gt;Umgang mit DateTime Feldern&lt;/h3&gt;
&lt;p&gt;Nun ist es aber in der Regel so, dass man einem Artikel kein &lt;code&gt;DateField&lt;/code&gt; sondern ein &lt;code&gt;DateTimeField&lt;/code&gt; zuweist, also die Zeit mitspeichert:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Füttern wir das Model wieder mit ein paar Testdaten:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;2009-03-25 12:30:00&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Irgendein Titel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;2009-03-24 10:25:00&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Irgendein Titel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;2009-03-24 8:56:00&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Irgendein Titel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Und führen unseren obigen Query aus:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;created&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="go"&gt;[{&amp;#39;counter&amp;#39;: 1, &amp;#39;created&amp;#39;: datetime.datetime(2009, 3, 24, 8, 56)},&lt;/span&gt;
&lt;span class="go"&gt; {&amp;#39;counter&amp;#39;: 1, &amp;#39;created&amp;#39;: datetime.datetime(2009, 3, 24, 10, 25)},&lt;/span&gt;
&lt;span class="go"&gt; {&amp;#39;counter&amp;#39;: 1, &amp;#39;created&amp;#39;: datetime.datetime(2009, 3, 25, 12, 30)}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Man sieht, das Ergebnis ist unbrauchbar. Logisch, denn es wird nach Datum &lt;em&gt;und&lt;/em&gt; Zeit gruppiert. Wir brauchen also nur das Datum aus dem DateTime-Objekt. In SQL gibt es dafür die Funktion &lt;code&gt;DATE(datetime-Feld)&lt;/code&gt; die das Datum extrahiert:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;day&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;entry_table&lt;/span&gt; &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;day&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Dummerweise liefert Django keinen Filter mit, um eine solche Aufgabe zu lösen. Es gibt zwar die &lt;a href="http://docs.djangoproject.com/en/dev/ref/models/querysets/#dates-field-kind-order-asc"&gt;dates&lt;/a&gt; Methode, um Zeitperioden aus einem Queryset zu extrahieren, diese liefert aber nur die DateTime-Objekte zurück, kein komplettes Queryset, dass für &lt;code&gt;annotate&lt;/code&gt; notwendig ist.&lt;/p&gt;
&lt;p&gt;Es muss also doch (etwas) rohes SQL her. Dafür ist die &lt;a href="http://docs.djangoproject.com/en/dev/ref/models/querysets/#extra-select-none-where-none-params-none-tables-none-order-by-none-select-params-none"&gt;extra&lt;/a&gt; Methode gedacht. Mit ihr kann man  rohes SQL in den Query &lt;em&gt;einsetzen&lt;/em&gt;. Folgendes Beispiel resultiert gleich:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;Queryset: Entry.objects.extra(select={&amp;#39;day&amp;#39;: &amp;#39;DATE(`created`)&amp;#39;})
SQL:      SELECT *, DATE(`created`) AS day FROM entry_table
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Zusammengefügt schaut unser Queryset jetzt so aus:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;day&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;DATE(`created`)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="go"&gt;    .values(&amp;#39;day&amp;#39;).annotate(counter=Count(&amp;#39;id&amp;#39;))&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;
&lt;span class="go"&gt;[{&amp;#39;counter&amp;#39;: 2, &amp;#39;day&amp;#39;: u&amp;#39;2009-03-24&amp;#39;}, &lt;/span&gt;
&lt;span class="go"&gt; {&amp;#39;counter&amp;#39;: 1, &amp;#39;day&amp;#39;: u&amp;#39;2009-03-25&amp;#39;}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Die Tage sind hierbei aber ein unicode-String, ein DateTime-Objekt wäre schöner. Mappen wir den Queryset noch einmal:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5
6&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;day&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;DATE(`created`)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="go"&gt;    .values(&amp;#39;day&amp;#39;).annotate(counter=Count(&amp;#39;id&amp;#39;))&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;counter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;counter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;day&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;day&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;%Y-%m-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;
&lt;span class="go"&gt;[{&amp;#39;counter&amp;#39;: 2, &amp;#39;day&amp;#39;: datetime.datetime(2009, 3, 24, 0, 0)},&lt;/span&gt;
&lt;span class="go"&gt; {&amp;#39;counter&amp;#39;: 1, &amp;#39;day&amp;#39;: datetime.datetime(2009, 3, 25, 0, 0)}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Da sind sie, die Tage an denen Beiträge hinzugefügt wurden inkl. deren Anzahl. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :-)" title=" :-)" class="smiley"/&gt;&lt;/p&gt;
&lt;p&gt;War diesmal vielleicht (noch) verworrener als sonst, wer Fragen dazu hat kann mir wie immer mailen oder in die Kommentare schreiben. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :-)" title=" :-)" class="smiley"/&gt;&lt;/p&gt;</description><pubDate>Wed, 08 Apr 2009 11:57:27 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/group-funktionen-querysets-django-11.html</guid></item><item><title>Automatisch lokalisierte Zeitformatierungen </title><link>http://www.freakydog.de/automatisch-lokalisierte-zeitformatierungen.html</link><description>&lt;p&gt;Ein nur knapp &lt;a href="http://docs.djangoproject.com/en/dev/topics/i18n/?from=olddocs#in-template-code"&gt;dokumentiertes&lt;/a&gt; Feature ist die Lokalisierung von String &lt;em&gt;innerhalb&lt;/em&gt; von Templatetags und Filtern. Dort reicht es, den betreffenden String in einen gettext-Shortcut zu setzen:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="kp"&gt;_&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hello world&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)|&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Dieser Token würde als &lt;code&gt;HELLO WORLD&lt;/code&gt; ausgegeben und natürlich auch mit den betreffenden &lt;em&gt;.po&lt;/em&gt; Dateien lokalisierbar sein. Richtig sinnvoll wird es beim &lt;code&gt;date&lt;/code&gt; Filter. Bisher hast du vielleicht das Datum fest vorgegeben:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;entry.published&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="s2"&gt;:&amp;quot;d.m.Y H:M:S&amp;quot;&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Funktioniert im Deutschen ganz gut, aber da ja bekannterweise jede Sprache das Datum und die Zeit irgendwie anders formatiert, ist es eine gute Idee, die Formatierung auch lokalisierbar anzubieten:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;entry.published&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kp"&gt;_&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;DATETIME_FORMAT&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Schaut ein wenig komisch aus, funktioniert aber. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :-)" title=" :-)" class="smiley"/&gt; Clevererweise kennt Django bereits den i18n-String &lt;code&gt;DATETIME_FORMAT&lt;/code&gt; und liefert für jede mitgelieferte Sprache die entsprechende Formatierung in den gettext-Katalogen mit. Je nach Locale-Einstellung ist die Ausgabe landestypisch:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;de-de: 21. März 2009, 20:24
en-us: March 21, 2009, 8:24 p.m.
pt-br: 21 de Março de 2009 às 20:24
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Neben &lt;code&gt;DATETIME_FORMAT&lt;/code&gt; werden auch gleich &lt;code&gt;DATE_FORMAT&lt;/code&gt;, &lt;code&gt;TIME_FORMAT&lt;/code&gt;, &lt;code&gt;YEAR_MONTH_FORMAT&lt;/code&gt; und &lt;code&gt;MONTH_DAY_FORMAT&lt;/code&gt; mitgeliefert. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :-)" title=" :-)" class="smiley"/&gt;&lt;/p&gt;</description><pubDate>Sat, 21 Mar 2009 19:35:58 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/automatisch-lokalisierte-zeitformatierungen.html</guid></item><item><title>Sichere Settings in der ServerError Site</title><link>http://www.freakydog.de/sichere-settings-der-servererror-site.html</link><description>&lt;p&gt;Manchmal lässt es sich nicht vermeiden, auf einem Live-System Djangos Fehler (Exception) Seite, den 500-View anzeigen zu lassen. Sie enthält den Traceback sowie den Umgebungsvariablen und allen Settings, Dinge die ein &lt;em&gt;normaler&lt;/em&gt; Besucher eigentlich nicht sehen soll.&lt;/p&gt;
&lt;p&gt;&lt;a rel="lightbox" href="http://static.mahner.org/assets/weblog/django/settings3g.png"&gt;&lt;img src="http://static.mahner.org/assets/weblog/django/settings3.png" alt=""/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In den Settings sind schon einige, sicherheitstechnisch riskante Felder gesternt, &lt;code&gt;DB_PASSWORD&lt;/code&gt; zum Beispiel. Fast jeder definiert aber eigene Settings, wie ich hier z.B. den Wert &lt;code&gt;TWITTER_USERNAME&lt;/code&gt; mit meinem &lt;a href="http://twitter.com/bartTC"&gt;Twitter-Namen&lt;/a&gt;.&lt;br /&gt;
&lt;/p&gt;
&lt;img src="http://static.mahner.org/assets/weblog/django/settings1.png" alt=""/&gt;

&lt;p&gt;Dieses Setting ist jetzt zwar nicht hochriskant aber es soll nur als  Beispiel dienen. Andere, geheimere Settings wären z.B. dein Flickr-API-Key. Jedenfalls, der zufällige Besucher auf so einer 500er Seite soll diesen Wert &lt;strong&gt;niemals&lt;/strong&gt; sehen.&lt;/p&gt;
&lt;p&gt;Eine Möglichkeit ist, den Namen &lt;em&gt;nicht&lt;/em&gt; komplett in Großbuchstaben schreiben, &lt;code&gt;twitter_username = 'bartTC'&lt;/code&gt; und schon wird er nicht mehr in dieser Liste auftauchen. Das funktioniert zwar wunderbar ... schaut aber komisch aus. Definitionen sollten in Großbuchstaben definiert werden. (Gibts ein PIP dazu?)&lt;/p&gt;
&lt;p&gt;Eine bessere Möglichkeit ist es, dem Namen der Funktion eines der Wörter &lt;code&gt;SECRET&lt;/code&gt;, &lt;code&gt;PASSWORD&lt;/code&gt; oder &lt;code&gt;PROFANITIES_LIST&lt;/code&gt; hinzuzufügen.&lt;/p&gt;
&lt;img src="http://static.mahner.org/assets/weblog/django/settings2.png" alt=""/&gt;

&lt;p&gt;Ein Wert wie &lt;code&gt;TWITTER_SECRET_USERNAME&lt;/code&gt; wird durch das vorhandene &lt;code&gt;SECRET&lt;/code&gt; nämlich ausgesternt. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :-)" title=" :-)" class="smiley"/&gt;&lt;/p&gt;
&lt;h3&gt;Weiter lesen&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Eric Holscher hat einen Beitrag geschrieben, der die 500 und 404 Seiten nur Superusern oder Usern mit der IP in &lt;code&gt;settings.INTERNAL_IPS&lt;/code&gt;, anzeigt: &lt;a href="http://ericholscher.com/blog/2008/nov/15/debugging-django-production-environments/"&gt;Debugging Django in Production Environments&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;</description><pubDate>Fri, 06 Mar 2009 22:32:32 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/sichere-settings-der-servererror-site.html</guid></item><item><title>Zugemüllt</title><link>http://www.freakydog.de/zugemullt.html</link><description>&lt;p&gt;Was mach ich am Wochenende?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ein Haus bauen.&lt;/li&gt;
&lt;li&gt;Ein Buch schreiben.&lt;/li&gt;
&lt;li&gt;Meinen Schreibtisch aufräumen.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dürfte alles etwa gleich lang dauern. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_unhappy.png" alt="Smiley:  :-(" title=" :-(" class="smiley"/&gt;&lt;/p&gt;</description><pubDate>Tue, 03 Mar 2009 22:31:40 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/zugemullt.html</guid></item><item><title>Sprachabhängige Template-Imports</title><link>http://www.freakydog.de/sprachabhangige-template-imports.html</link><description>&lt;p&gt;Zuerst die Erklärung, der vollständige Code folgt am Ende.&lt;/p&gt;
&lt;p&gt;Um Internationalisierung (i18n) bzw. Übersetzung in Django-Templates zu bringen, gibt es verschiedene Möglichkeiten, allen voran die Möglichkeit, Strings mittels &lt;code&gt;trans&lt;/code&gt; oder &lt;code&gt;blocktrans&lt;/code&gt; zu markieren und in den entsprechenden gettext-Dateien zu übersetzen.&lt;/p&gt;
&lt;p&gt;Das funktioniert gut und hat sich bewährt, solange es sich um Strings handelt. Möchte man eine komplette Seite übersetzen, inkl. Bildern und HTML-Code, wird es schwieriger, mal abgesehen davon, dass ich HTML in gettext-Strings einfach häßlich finde. &lt;/p&gt;
&lt;p&gt;Eine Möglichkeit ist es, &lt;code&gt;ifequal&lt;/code&gt; mit dem aktuellen Sprachcode des Users (&lt;code&gt;LANGUAGE_CODE&lt;/code&gt;) zu vergleichen:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;ifequal&lt;/span&gt; &lt;span class="nv"&gt;LANGUAGE_CODE&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;de&amp;quot;&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;    &amp;lt;p&amp;gt;Hier ganz viel deutscher Text.&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;    &amp;lt;p&amp;gt;Here is a lot of english text, the default language.&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Wird der Inhalt zu lang, kann man die Texte jeweils in eigene Templates auslagern und mittels &lt;code&gt;include&lt;/code&gt; importieren:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;ifequal&lt;/span&gt; &lt;span class="nv"&gt;LANGUAGE_CODE&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;de&amp;quot;&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;include&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;vieltext_deutsch.html&amp;quot;&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;include&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;vieltext_english.html&amp;quot;&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Hier setzt der unten gezeigte Templatetag an. Er arbeitet prinzipiell wie der Standard-Include-Tag aber importiert automatisch das Template mit der jeweiligen Sprache. &lt;/p&gt;
&lt;p&gt;Der Templatename muss dabei das Format &lt;code&gt;&amp;lt;templatename&amp;gt;.&amp;lt;languagecode&amp;gt;&lt;/code&gt; als Dateinamen besitzen. Angenommen unser User ist mit deutscher Spracheinstellung unterwegs und sein &lt;code&gt;LANGUAGE_CODE&lt;/code&gt; wäre &lt;code&gt;de&lt;/code&gt;, der Aufruf:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;langinclude&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;meintext.html&amp;quot;&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;würde hier zuerst das Template &lt;code&gt;meintext.html.de&lt;/code&gt; suchen und importierten. Schlägt dies fehl, importiert es wie gewohnt das Template &lt;code&gt;meintext.html&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Hier nun der Templatetag:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.template&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Library&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.template&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TemplateSyntaxError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TemplateDoesNotExist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Variable&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.template.loader_tags&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;IncludeNode&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.template.loader&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_template&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;

&lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Library&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConstantLanguageIncludeNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;template_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;template_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;template_path&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
            &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;template_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;LANGUAGE_CODE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;TemplateDoesNotExist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ne"&gt;KeyError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;template_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TEMPLATE_DEBUG&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_language_include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Looks up for a template based on the template-name plus the current users language code.&lt;/span&gt;
&lt;span class="sd"&gt;    Loads the template and renders it with the current context.&lt;/span&gt;

&lt;span class="sd"&gt;    Example::&lt;/span&gt;

&lt;span class="sd"&gt;        {% langinclude &amp;quot;foo/some_include.html&amp;quot; %}&lt;/span&gt;

&lt;span class="sd"&gt;    Based on the users LANGUAGE_CODE, assumed we have &amp;#39;de&amp;#39;, it tries to render the&lt;/span&gt;
&lt;span class="sd"&gt;    template &amp;#39;foo/some_include.html.de&amp;#39;. If that doesn&amp;#39;t exists, it renders the &lt;/span&gt;
&lt;span class="sd"&gt;    template &amp;#39;foo/some_include.html&amp;#39;. This is the default behavior of the include-Tag.&lt;/span&gt;

&lt;span class="sd"&gt;    Basically this is a shortcut for the following code, just with a fallback for the&lt;/span&gt;
&lt;span class="sd"&gt;    default template::&lt;/span&gt;

&lt;span class="sd"&gt;        {% ifequal LANGUAGE_CODE &amp;quot;de&amp;quot; %}&lt;/span&gt;
&lt;span class="sd"&gt;            {% include &amp;quot;foo/some_include.html.de&amp;quot; %}&lt;/span&gt;
&lt;span class="sd"&gt;        {% else %}&lt;/span&gt;
&lt;span class="sd"&gt;            {% include &amp;quot;foo/some_include.html&amp;quot; %}&lt;/span&gt;
&lt;span class="sd"&gt;        {% endifequal %}&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;TemplateSyntaxError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%r&lt;/span&gt;&lt;span class="s"&gt; tag takes one argument: the name of the template to be included&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ConstantLanguageIncludeNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;IncludeNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;langinclude&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;do_language_include&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Der Quellcode auf &lt;a href="http://www.djangosnippets.org/snippets/1341/"&gt;djangosnippets.org&lt;/a&gt;.&lt;/p&gt;</description><pubDate>Wed, 25 Feb 2009 18:59:13 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/sprachabhangige-template-imports.html</guid></item><item><title>Tägliche Postfix-Helfer</title><link>http://www.freakydog.de/tagliche-postfix-helfer.html</link><description>&lt;p&gt;Hier ist ein kurzer Überblick über Tools, die einem Postfix-Postmaster das tägliche Leben erleichtern. Alle Tools sind auf Debian und Ubuntu-Systemen in den Standard-Paketquellen enthalten. &lt;/p&gt;
&lt;h3&gt;pflogsumm - Tägliche Zusammenfassung der Logfileaktivität&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;pflogsumm&lt;/em&gt; erstellt eine detaillierte Übersicht über die Logfileaktivität des Tages oder des Vortages. Dazu gehört die Zusammenfassung aller Mails (ausgehend, eingehend, rejected etc.), deren Gründe, die Warnungen, Fehler und Panikattacken von Postfix und noch vieles mehr. Der tägliche pflogsumm-Bericht ist Pflicht für jeden, der einen Überblick über seine Mailserveraktivität haben möchte!&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;Postfix Log Zusammenfassung fuer Feb 18

Zusammenfassung
---------------
Nachrichten

  3168   empfangen
  1709   zugestellt
    24   weitergeleitet
    23   zurueckgewiesen  (83  Zurueckweisungen)
   113   zurueckgeworfen
 36233   abgelehnt (95%)
     0   Ablehnungswarnungen
     0   wartend
     0   verworfen (0%)

 440838k  bytes empfangen
 475401k  bytes zugestellt
    770   Sender
    553   sendende hosts/domains
    389   Empfaenger
    183   empfangende hosts/domains

 ... [5000 weitere Statistikzeilen]
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Am besten installiert man sich einen Crontab, der einem jeden morgen die Statistiken des Vortages zumailt. Hier wird morgens um 06:00 der Bericht des Vortages erstellt:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt; 0   6   *   *   *    /usr/sbin/pflogsumm -d yesterday /var/log/mail.info /var/log/mail.info.0 | mail -s &amp;quot;Mailstatistiken&amp;quot; postmaster@localhost
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;ul&gt;
&lt;li&gt;Homepage: &lt;a href="http://jimsun.linxnet.com/postfix_contrib.html"&gt;jimsun.linxnet.com/postfix_contrib.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Deutsche Version von Patrick Koetter: &lt;a href="http://postfix.state-of-mind.de/patrick.koetter/pflogsumm/"&gt;postfix.state-of-mind.de/patrick.koetter/pflogsumm/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Installation: &lt;code&gt;apt-get install pflogsumm&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Mailgraph - Mailstatistiken in Bunt&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Mailgraph&lt;/em&gt; ist ein Dämon der permanent das mail.log überwacht und Statistiken über empfangene und versandte Mails in einer &lt;a href="http://oss.oetiker.ch/rrdtool/"&gt;RoundRobin-Datenbank&lt;/a&gt; speichert. Mit dabei ist das Perlscript &lt;em&gt;mailgraph.cgi&lt;/em&gt; das aus diesen Daten wunderschöne Grafiken erstellt:&lt;/p&gt;
&lt;img src="http://static.mahner.org/assets/weblog/postfix_mailgraph.jpg" alt=""&gt;

&lt;p&gt;Das Script befindet sich von Haus aus in &lt;code&gt;/var/lib/cgi-bin/mailgraph.cgi&lt;/code&gt;. Eventuell musst du es erst bearbeiten und die Pfade zur RRD-Datenbank (&lt;code&gt;/var/lib/mailgraph/&lt;/code&gt;) anpassen.
Prüft nach der Installation auch noch einmal den Pfad zum Logfile; in der &lt;code&gt;/etc/default/mailgraph&lt;/code&gt; lautet der Pfad für ein Standard-Setup &lt;code&gt;MAIL_LOG=/var/log/mail.info&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ob und inwiefern das sinnvoll ist, mag jeder für sich selbst entscheiden. Ich nutze es als schnellen Blick um mich zu vergewissern, dass mit dem Server alles rund läuft.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Homepage: &lt;a href="http://mailgraph.schweikert.ch/"&gt;mailgraph.schweikert.ch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Installation: &lt;code&gt;apt-get install mailgraph; /etc/init.d/mailgraph start&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;qshape - Queue-Statistiken&lt;/h3&gt;
&lt;p&gt;Mit Postfix wird das kleine Tool &lt;em&gt;qshape&lt;/em&gt; mitgeliefert, bei Debian-Installationen ist es unter &lt;code&gt;/usr/sbin/qshape&lt;/code&gt; zu finden. qshape zeigt eine Summierung der Anzahl der E-Mails in der Queue. Am interessantesten ist wohl die deferred-Queue:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5
6
7&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;# qshape deferred
                T  5 10 20 40 80 160 320 640 1280 1280+
         TOTAL 59  0  0  0  0  1   0  56   2    0     0
   example.com 56  0  0  0  0  0   0  56   0    0     0
          h.cx  1  0  0  0  0  1   0   0   0    0     0
     surfeu.de  1  0  0  0  0  0   0   0   1    0     0
couttsbath.com  1  0  0  0  0  0   0   0   1    0     0
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;So erhält man schnell einen Überblick, was auf dem Server hängt.&lt;/p&gt;
&lt;h3&gt;pfqueue - Frontend für die Queues&lt;/h3&gt;
&lt;p&gt;Eine alternative Oberfläche für qshape ist &lt;em&gt;pfqueue&lt;/em&gt;. Dort wechselt man mit den Tasten 1, 2, 3 und 4 zwischen den Queues (deferred, active, incoming, hold). Mails kann man direkt mit &lt;em&gt;d&lt;/em&gt; löschen, mit &lt;em&gt;r&lt;/em&gt; requeuen und vieles mehr.&lt;/p&gt;
&lt;img src="http://static.mahner.org/assets/weblog/postfix_pfqueue.jpg" alt=""&gt;

&lt;p&gt;pfqueue ist vom Handling her ungewöhnlich aber sehr mächtig. Am besten man druckt sich vor der Benutzung die Manpage aus.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Homepage: &lt;a href="http://pfqueue.sourceforge.net/"&gt;pfqueue.sourceforge.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Installation: &lt;code&gt;apt-get install pfqueue&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;</description><pubDate>Thu, 19 Feb 2009 21:11:33 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/tagliche-postfix-helfer.html</guid></item><item><title>Variable Spamchecks mit Postfix trotz content_filter</title><link>http://www.freakydog.de/variable-spamchecks-mit-postfix-trotz-content_filter.html</link><description>&lt;p&gt;In den letzten Tagen hatte ich Gelegenheit meine Postfix-Konfiguration neu aufzubauen. Bisher hatte ich amavisd-new als Frontend-Filter für Spamassassin benutzt, in Ubuntu 8.04 funktioniert die Installation allerdings nicht mehr:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5
6&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;Starting amavisd: ERROR: MISSING REQUIRED BASIC MODULES:
  Compress::Zlib
BEGIN failed--compilation aborted at /usr/sbin/amavisd-new line 171.
(failed).
invoke-rc.d: initscript amavis, action &amp;quot;start&amp;quot; failed.
WARNING: Starting amavisd-new failed. Please check your configuration.
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Das CPAN-Modul respektive &lt;code&gt;libcompress-zlip-perl&lt;/code&gt; ist installiert, vielleicht weis ein Leser ja Rat.&lt;/p&gt;
&lt;p&gt;Auf jeden Fall muss eine Alternative her. Im Spamassassin-Wiki ist &lt;a href="http://wiki.apache.org/spamassassin/IntegratedSpamdInPostfix"&gt;eine Methode beschrieben&lt;/a&gt;, wie man Spamassassin als globalen &lt;code&gt;content_filter&lt;/code&gt; in Postfix einbindet. Prinzipiell ganz gut, allerdings werden so &lt;em&gt;alle&lt;/em&gt; Mails gefiltert, die Postfix passieren. Ich möchte aber einige User und auch ganze Domains ausschließen.&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;Zu allererst muss sichergestellt sein, dass Spamassassin auch installiert ist, auf Debian-Systemen reicht ein &lt;code&gt;apt-get install spamassassin&lt;/code&gt;. Starte den Dämon mittels &lt;code&gt;/etc/init.d/spamassassin start&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In der &lt;code&gt;/etc/postfix/master.conf&lt;/code&gt; wird ein neuer Transport &lt;code&gt;spamcheck&lt;/code&gt; hinzugefügt:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;spamcheck unix -     n       n       -       -       pipe
    user=nobody argv=/usr/bin/spamc -f -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;In die &lt;code&gt;/etc/postfix/main.conf&lt;/code&gt; werden zwei Änderungen vorgenommen. Das Recipient-Limit für den genannten Spamassassin-Transport auf 1 setzen, damit jeweils nur eine E-Mail durchgereicht wird. Und als vorletzten Parameter &amp;ndash; vor dem abschließenden &lt;code&gt;permit&lt;/code&gt; &amp;ndash; wird ein access_check auf die Datei &lt;code&gt;/etc/postfix/access_spamassassin&lt;/code&gt; hinzugefügt.&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;spamcheck_destination_recipient_limit = 1

smtpd_recipient_restrictions = 
    reject_non_fqdn_recipient, 
    reject_non_fqdn_sender,  
    permit_mynetworks,        
    # ... RBL Listen, etc
    reject_unauth_destination,
    # Hier werden die Empfänger geprüft
    check_recipient_access hash:/etc/postfix/access_spamassassin,
    permit
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Zu guter letzt lege die Datei &lt;code&gt;/etc/postfix/access_spamassassin&lt;/code&gt; an. Ihr ihr wird penibel definiert, welche Empfänger letztendlich durch Spamassassin geprüft werden:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;example.org       FILTER spamcheck:dummy
user@example.com  OK
example.com       FILTER spamcheck:dummy
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Der Aufbau ist einfach, an erster Stelle steht die Empfängeradresse oder Domain gefolgt von einem Leerzeichen oder Tab und dem Wort &lt;code&gt;FILTER spamcheck:dummy&lt;/code&gt;. &lt;code&gt;spamcheck&lt;/code&gt; ist unser oben definierter Transport, &lt;code&gt;dummy&lt;/code&gt; gibt den nächsten Hop an, den die Mail nehmen soll. In unserem Fall ist es ja der localhost, in diesem Fall reicht einfach &lt;code&gt;dummy&lt;/code&gt;. Wenn einzelne Benutzer ausgeschlossen werden sollen, setze ein OK dahinter, achte aber auf die Reihenfolge.&lt;/p&gt;
&lt;p&gt;Nicht vergessen, danach ein &lt;code&gt;postmap hash:/etc/postfix/access_spamassassin&lt;/code&gt; auszuführen.&lt;/p&gt;
&lt;h3&gt;Was passiert bei diesem Setup?&lt;/h3&gt;
&lt;img src="http://static.mahner.org/assets/weblog/postfixs.png" alt=""/&gt;

&lt;p&gt;Ein fremder Mailserver liefert die E-Mail ein, sofern sie alle Checks besteht passiert sie den Spamassassin-Recipient-Check und wird weitergeleitet zu &lt;code&gt;spamc&lt;/code&gt; (rote Linie), einem Helferprogramm dass den Mailbody mit Spamassassin prüft. &lt;code&gt;spamc&lt;/code&gt; schickt danach die Nachricht über das lokale &lt;code&gt;sendmail&lt;/code&gt; Programm wieder an Postfix (blaue Linie).&lt;/p&gt;
&lt;p&gt;Da die Nachricht diesmal vom lokalen System kommt, durchläuft sie nicht alle Checks sondern wird schon bei der Prüfung auf lokale IPs &lt;code&gt;permit_mynetworks&lt;/code&gt; an den Haupttransport (&lt;code&gt;virtual_transport&lt;/code&gt;, &lt;code&gt;local_transport&lt;/code&gt;, etc.), wie zum Beispiel Procmail, abgegeben.&lt;/p&gt;
&lt;p&gt;Steht die Zieladresse erst garnicht in der access-Datei, wird sie auch nicht mit Spamassassin geprüft und mittels des abschließenden &lt;code&gt;permit&lt;/code&gt; an den originalen Haupttransport weitergeleitet (graue Linie).&lt;/p&gt;
&lt;h3&gt;Nachteile&lt;/h3&gt;
&lt;p&gt;Der große Nachteil bei diesem Setup ist, dass die Mail, bevor die &lt;code&gt;spamc&lt;/code&gt; zugestellt wird, die Postfix-Queue verlässt. Es ist danach nicht mehr möglich, die Mail zu rejecten &amp;ndash; jedenfalls nicht ohne &lt;a href="http://de.wikipedia.org/wiki/Backscatter"&gt;Backscatter&lt;/a&gt; zu erzeugen.&lt;/p&gt;
&lt;p&gt;Gerade das ist der große Vorteil von Amavis, wenn er als smtpd_proxy_filter eingebunden ist. So lassen sich dort alle oder bestimmte Mails ab einer gewissen Score rejecten. Da ich aber recht aggressiv Blacklisten einsetze ist die False-Positive Rate bei Spamassassin-erkannten Mails recht hoch, so dass ich damit leben kann, alle Mails letztendlich anzunehmen.&lt;/p&gt;
&lt;p&gt;In der nächsten Zukunft werde ich mir mal Gedanken machen (sprich Google fragen), ob man das oben gezeigte Schema nicht auch als smptd_proxy_filter verbauen kann.&lt;/p&gt;
&lt;h3&gt;Meine Postfix-Konfiguration&lt;/h3&gt;
&lt;p&gt;Konfigurationsdateien sind ja mindestens so spannend wie Screenshots. Da ich keine Sicherheitsbedenken habe sie zu veröffentlichen und sie dazu noch kommiertiert ist; hier ist meine komplette Postfix-main.conf. Vielleicht findet noch jemand Ideen darin.&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;# --------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# Base Server Config&lt;/span&gt;
&lt;span class="c"&gt;# --------------------------------------------------------------------&lt;/span&gt;

&lt;span class="na"&gt;myhostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;schwammkopf.mahner.org&lt;/span&gt;
&lt;span class="na"&gt;mydestination&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;localhost # myhostname steht in den virtual domains&lt;/span&gt;
&lt;span class="na"&gt;myorigin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/etc/mailname&lt;/span&gt;
&lt;span class="na"&gt;mynetworks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;127.0.0.0/8, 88.198.109.76, [::ffff:127.0.0.0]/104, [::1]/128 &lt;/span&gt;
&lt;span class="na"&gt;inet_interfaces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;
&lt;span class="na"&gt;smtpd_banner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$myhostname ESMTP - Der schwammige Mailserver&lt;/span&gt;
&lt;span class="na"&gt;append_dot_mydomain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;

&lt;span class="c"&gt;# Biff Tannen? Niemals!&lt;/span&gt;
&lt;span class="na"&gt;biff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;

&lt;span class="c"&gt;# foo+bar@example.com, brauch ich nicht&lt;/span&gt;
&lt;span class="na"&gt;recipient_delimiter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="s"&gt;# Deutsche Bounce-Files&lt;/span&gt;
&lt;span class="na"&gt;bounce_template_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/etc/postfix/bounce-templates/bounce.de-DE.cf&lt;/span&gt;

&lt;span class="c"&gt;# Systembenutzer&lt;/span&gt;
&lt;span class="na"&gt;alias_maps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;hash:/etc/aliases&lt;/span&gt;
&lt;span class="na"&gt;alias_database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;hash:/etc/aliases&lt;/span&gt;

&lt;span class="c"&gt;# --------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# SMTP Errorlimits&lt;/span&gt;
&lt;span class="c"&gt;# --------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Maximale E-Mail Größe (TODO: auf 2kb setzen) 50MB&lt;/span&gt;
&lt;span class="na"&gt;message_size_limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;52428800&lt;/span&gt;

&lt;span class="c"&gt;# Wann soll eine Warnung erfolgen, wenn die Mail&lt;/span&gt;
&lt;span class="c"&gt;# nicht zugestellt werden kann?&lt;/span&gt;
&lt;span class="na"&gt;delay_warning_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1h&lt;/span&gt;

&lt;span class="c"&gt;# Wie lange soll die Mail in der Queue bleiben,&lt;/span&gt;
&lt;span class="c"&gt;# wenn keine Zustellung möglich ist?&lt;/span&gt;
&lt;span class="na"&gt;maximal_queue_lifetime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1d &lt;/span&gt;
&lt;span class="na"&gt;bounce_queue_lifetime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1d&lt;/span&gt;

&lt;span class="c"&gt;# Max und Min Zeit wenn der Connect fehlschläft (auch 4xx)&lt;/span&gt;
&lt;span class="c"&gt;# Greylisting ist typischerweise auf 5min eingestellt&lt;/span&gt;
&lt;span class="na"&gt;minimal_backoff_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;330s &lt;/span&gt;
&lt;span class="na"&gt;maximal_backoff_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;8000s&lt;/span&gt;

&lt;span class="c"&gt;# Wie lange nach dem HELO warten?&lt;/span&gt;
&lt;span class="na"&gt;smtp_helo_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;60s&lt;/span&gt;

&lt;span class="c"&gt;# Wie viele Adressen in einer Nachricht maximal?&lt;/span&gt;
&lt;span class="c"&gt;# Mailman ftw!&lt;/span&gt;
&lt;span class="na"&gt;smtpd_recipient_limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;16&lt;/span&gt;

&lt;span class="c"&gt;# Wie viele Fehler bis wir müde werden und wie viele,&lt;/span&gt;
&lt;span class="c"&gt;# bis komplett blockiert wird&lt;/span&gt;
&lt;span class="na"&gt;smtpd_soft_error_limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;3 &lt;/span&gt;
&lt;span class="na"&gt;smtpd_hard_error_limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;12&lt;/span&gt;
&lt;span class="na"&gt;smtpd_error_sleep_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1s&lt;/span&gt;

&lt;span class="c"&gt;# Ein nettes Hallo beim Connect bitte &lt;/span&gt;
&lt;span class="na"&gt;smtpd_helo_required&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;

&lt;span class="c"&gt;# Spammerzeit vergeuden &lt;/span&gt;
&lt;span class="na"&gt;smtpd_delay_reject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;no &lt;/span&gt;
&lt;span class="na"&gt;disable_vrfy_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;

&lt;span class="c"&gt;# --------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# SMTP Checks &amp;amp; Restrictions&lt;/span&gt;
&lt;span class="c"&gt;# --------------------------------------------------------------------&lt;/span&gt;

&lt;span class="na"&gt;smtpd_client_restrictions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;       
&lt;span class="s"&gt;smtpd_helo_restrictions =                               &lt;/span&gt;
&lt;span class="na"&gt;smtpd_sender_restrictions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
&lt;span class="s"&gt;smtpd_data_restrictions =&lt;/span&gt;
&lt;span class="na"&gt;smtpd_recipient_restrictions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="s"&gt;check_client_access hash:/etc/postfix/access/client,&lt;/span&gt;
                                &lt;span class="err"&gt;check_helo_access&lt;/span&gt; &lt;span class="err"&gt;hash:/etc/postfix/access/helo,&lt;/span&gt;
                                &lt;span class="err"&gt;check_sender_access&lt;/span&gt; &lt;span class="err"&gt;hash:/etc/postfix/access/sender,&lt;/span&gt;
                                &lt;span class="err"&gt;check_recipient_access&lt;/span&gt; &lt;span class="err"&gt;hash:/etc/postfix/access/recipient,&lt;/span&gt;
                                &lt;span class="c"&gt;# Blocken wenn Pipelining nicht angefragt wurde&lt;/span&gt;
                                &lt;span class="err"&gt;reject_unauth_pipelining,&lt;/span&gt;
                                &lt;span class="c"&gt;# Auf anständige Syntax prüfen&lt;/span&gt;
                                &lt;span class="err"&gt;reject_non_fqdn_recipient,&lt;/span&gt; 
                                &lt;span class="err"&gt;reject_non_fqdn_sender,&lt;/span&gt;  
                                &lt;span class="c"&gt;# DNS Checks&lt;/span&gt;
                                &lt;span class="err"&gt;reject_unknown_recipient_domain,&lt;/span&gt;
                                &lt;span class="err"&gt;reject_unknown_sender_domain,&lt;/span&gt;
                                &lt;span class="c"&gt;# Eigene Nutzer erlauben&lt;/span&gt;
                                &lt;span class="err"&gt;permit_mynetworks,&lt;/span&gt;        
                                &lt;span class="err"&gt;permit_sasl_authenticated,&lt;/span&gt;          
                                &lt;span class="c"&gt;# RBL Blacklisten&lt;/span&gt;
                                &lt;span class="err"&gt;reject_rbl_client&lt;/span&gt; &lt;span class="err"&gt;zen.spamhaus.org,&lt;/span&gt;
                                &lt;span class="err"&gt;reject_rbl_client&lt;/span&gt; &lt;span class="err"&gt;ix.dnsbl.manitu.net,&lt;/span&gt;
                                &lt;span class="err"&gt;reject_rbl_client&lt;/span&gt; &lt;span class="err"&gt;bl.spamcop.net,&lt;/span&gt;
                                &lt;span class="err"&gt;reject_rbl_client&lt;/span&gt; &lt;span class="err"&gt;blackhole.securitysage.com,&lt;/span&gt;
                                &lt;span class="c"&gt;# Greylisting   &lt;/span&gt;
                                &lt;span class="err"&gt;check_policy_service&lt;/span&gt; &lt;span class="err"&gt;inet:127.0.0.1:60000,&lt;/span&gt;
                                &lt;span class="c"&gt;# Relaying&lt;/span&gt;
                                &lt;span class="err"&gt;reject_unlisted_recipient,&lt;/span&gt;
                                &lt;span class="err"&gt;reject_unauth_destination,&lt;/span&gt;
                                &lt;span class="c"&gt;# Spamassassin Content Filter&lt;/span&gt;
                                &lt;span class="err"&gt;check_recipient_access&lt;/span&gt; &lt;span class="err"&gt;hash:/etc/postfix/access/spamassassin,&lt;/span&gt;
                                &lt;span class="c"&gt;# Wer nicht in spamassassin steht, ist durch                       &lt;/span&gt;
                                &lt;span class="err"&gt;permit&lt;/span&gt;

&lt;span class="c"&gt;# --------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# TLS Empfang/Versand&lt;/span&gt;
&lt;span class="c"&gt;# --------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# smptd (Mailempfang)&lt;/span&gt;
&lt;span class="na"&gt;smtpd_tls_security_level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;may&lt;/span&gt;
&lt;span class="na"&gt;smtpd_tls_cert_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/etc/postfix/certs/cert.pem&lt;/span&gt;
&lt;span class="na"&gt;smtpd_tls_key_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/etc/postfix/certs/key.pem&lt;/span&gt;
&lt;span class="na"&gt;smtpd_tls_CAfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/etc/postfix/certs/CAcert.pem&lt;/span&gt;
&lt;span class="na"&gt;smtpd_tls_loglevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt;

&lt;span class="c"&gt;# Bei SSL-Verbindung diese im received-Header markieren&lt;/span&gt;
&lt;span class="na"&gt;smtpd_tls_received_header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;

&lt;span class="c"&gt;# smtp (Mailversand)&lt;/span&gt;
&lt;span class="na"&gt;smtp_tls_security_level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;may&lt;/span&gt;
&lt;span class="na"&gt;smtp_tls_cert_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/etc/postfix/certs/cert.pem&lt;/span&gt;
&lt;span class="na"&gt;smtp_tls_key_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/etc/postfix/certs/key.pem&lt;/span&gt;
&lt;span class="na"&gt;smtp_tls_CAfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/etc/postfix/certs/CAcert.pem&lt;/span&gt;
&lt;span class="na"&gt;smpt_tls_loglevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt;

&lt;span class="c"&gt;# --------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# SASL (SMTP-Auth)&lt;/span&gt;
&lt;span class="c"&gt;# --------------------------------------------------------------------&lt;/span&gt;

&lt;span class="na"&gt;smtpd_sasl_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dovecot&lt;/span&gt;
&lt;span class="na"&gt;smtpd_sasl_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;private/auth&lt;/span&gt;
&lt;span class="na"&gt;smtpd_sasl_auth_enable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;span class="na"&gt;smtpd_sasl_security_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;noanonymous&lt;/span&gt;

&lt;span class="c"&gt;# --------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# Transport and Mapping&lt;/span&gt;
&lt;span class="c"&gt;# --------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Dovecot transport&lt;/span&gt;
&lt;span class="na"&gt;virtual_transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;dovecot&lt;/span&gt;
&lt;span class="na"&gt;dovecot_destination_recipient_limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt;

&lt;span class="c"&gt;# Spamassassin Transport in den recipient restrictions&lt;/span&gt;
&lt;span class="na"&gt;spamassassin_destination_recipient_limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt;

&lt;span class="na"&gt;virtual_uid_maps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;static:5000&lt;/span&gt;
&lt;span class="na"&gt;virtual_gid_maps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;static:5000&lt;/span&gt;

&lt;span class="na"&gt;virtual_mailbox_domains&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;mysql:/etc/postfix/mysql/virtual-mailbox-domains.cf&lt;/span&gt;
&lt;span class="na"&gt;virtual_mailbox_maps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;mysql:/etc/postfix/mysql/virtual-mailbox-maps.cf&lt;/span&gt;
&lt;span class="na"&gt;virtual_alias_maps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;mysql:/etc/postfix/mysql/virtual-alias-maps.cf,&lt;/span&gt;
                     &lt;span class="err"&gt;mysql:/etc/postfix/mysql/email2email.cf&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</description><pubDate>Thu, 19 Feb 2009 00:05:05 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/variable-spamchecks-mit-postfix-trotz-content_filter.html</guid></item><item><title>Plesk 8.x und 9.x: Keine Rechte zum Datenbank-Anlegen trotz Admin-Account</title><link>http://www.freakydog.de/plesk-8x-und-9x-keine-rechte-zum-datenbank-anlegen-trotz-admin-account.html</link><description>&lt;p&gt;In Plesk 8.x und auch noch in Plesk 9.0.x erscheint eine Fehlermeldung, wenn man eine neue Datenbank anlegen will:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Fehler: Parallels Plesk Panel kann keine Verbindung zum Datenbankserver herstellen, da kein gültiger Administrator-Benutzername und -Passwort angegeben wurden.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a rel="lightbox" href="http://static.mahner.org/assets/weblog/pleskdb.jpg"&gt;&lt;img src="http://static.mahner.org/assets/weblog/pleskdbk.jpg" alt=""/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Großartige Fehlermeldung. Natürlich sind die Rechte vorhanden und die Limits nicht ausgereizt. Der Fehler tritt nur auf, wenn man einen Webkit-Browser bzw. Safari benutzt. Also am einfachsten für diese Aufgabe zu Firefox wechseln.&lt;/p&gt;
&lt;p&gt;Mit Google-Chrome tritt der Fehler nicht auf, wohl weil es eine andere Javascript-Engine als Safari nutzt. Was wiederum bedeutet, dass Plesk wohl die DB-Zugangsdaten irgendwo im Javascript mitschleift und dort dann nicht findet. Alles sehr komisch.&lt;/p&gt;</description><pubDate>Sun, 08 Feb 2009 16:26:17 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/plesk-8x-und-9x-keine-rechte-zum-datenbank-anlegen-trotz-admin-account.html</guid></item><item><title>Ordnung im Medienordner: Dynamische Upload-Pfade</title><link>http://www.freakydog.de/ordnung-im-medienordner-dynamische-upload-pfade.html</link><description>&lt;p&gt;Djangos &lt;code&gt;FileField&lt;/code&gt; und das davon abgeleitete &lt;code&gt;ImageField&lt;/code&gt; Feld enthalten ein &lt;a href="http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.FileField.upload_to"&gt;upload_to-Argument&lt;/a&gt; das angibt, wohin die Datei im statischen Medienordner gespeichert werden soll:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5
6
7
8&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ImageField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;images/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__unicode__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_absolute_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Ein hochgeladenes Bild wäre dann über die URL &lt;code&gt;/media/images/filename.jpg&lt;/code&gt; zu erreichen. &lt;code&gt;/media/&lt;/code&gt; ist der URL-Pfad zu den statischen Medien, der zuvor in der &lt;code&gt;settings.py&lt;/code&gt; mit &lt;code&gt;MEDIA_URL&lt;/code&gt; definiert wurde.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;upload_to&lt;/code&gt; muss aber kein festgelegter Pfad sein, es kann auch ein &lt;code&gt;callable&lt;/code&gt;, also eine Funktion oder jede Klasse die ein &lt;code&gt;__call__&lt;/code&gt; Objekt bietet, verwerten.&lt;/p&gt;
&lt;p&gt;Wozu braucht man das? Immer dann, wenn der Pfad dynamisch sein soll. Im folgenden Beispiel soll eine hochgeladene Datei in mehreren Unterordnern abgelegt werden, wobei die Buchstaben der Unterordner die Anfangsbuchstaben der Datei sind, Huh, kompliziert? Gar nicht:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bilder_in_unterordnern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rsplit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c"&gt;# Dateiname ohne Extension muss&lt;/span&gt;
                                          &lt;span class="c"&gt;# mindestens 3 Zeichen besitzen&lt;/span&gt;
        &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;images/&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                                           &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ImageField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bilder_in_unterordnern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Das &lt;code&gt;upload_to&lt;/code&gt; Argument enthält den Namen einer Funktion, die den neuen Dateinamen (inklusive Pfad) zurück gibt. Beachte aber, dass das Funktionsargument hier keine Klammern besitzt, sonst würde sie nur einmal, beim Starten des Webservers, ausgeführt werden.&lt;/p&gt;
&lt;p&gt;Diese callable-Funktion besitzt immer zwei Argumente, die aktuelle Instanz des Modelobjekts und den originalen Dateinamen. Der Rückgabewert ist der neue Dateiname.&lt;/p&gt;
&lt;p&gt;Bilder die mit dem obigen Schema hochgeladen werden, würden dabei diese Pfade erhalten:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bild.jpg &amp;gt; /media/images/b/i/l/bild.jpg&lt;/li&gt;
&lt;li&gt;foobar.jpg &amp;gt; /media/images/f/o/o/foobar.jpg&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Schon ganz hübsch. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :)" title=" :)" class="smiley"/&gt; Richtig sinnvoll wird es aber erst, wenn der Bildpfad Werte aus der selben oder gar einer anderen Model-Instanz enthalten soll.&lt;/p&gt;
&lt;p&gt;Angenommen die Bilder sind mit einem Artikel (von einem Weblog, natürlich) verknüpft und sollen als Dateipfad den Slug des verknüpften Artikels enthalten, so kann das Callable auf die aktuelle Model-Instanz und dessen Verknüpfungen zugreifen:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bild_mit_articleslug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;images/&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blank&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SlugField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__unicode__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ImageField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bild_mit_articleslug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Angenommen unser Artikel hat den Titel &lt;em&gt;"Mein neuer Blogbeitrag"&lt;/em&gt; so wären die Dateinamen so:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bild.jpg &amp;gt; /media/images/mein-neuer-blogbeitrag/bild.jpg&lt;/li&gt;
&lt;li&gt;foobar.jpg &amp;gt; /media/images/mein-neuer-blogbeitrag/foobar.jpg&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So schafft man Ordnung im Dateisystem. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :)" title=" :)" class="smiley"/&gt;&lt;/p&gt;
&lt;h3&gt;Ausnahme Primärschlüssel&lt;/h3&gt;
&lt;p&gt;Eine Ausnahme ist, wenn der Dateiname des Bildes den Primärschlüssel des Objekts enthalten soll. Da, bei einem Insert, der Primärschlüssel noch nicht gesetzt ist, existiert dieses Attribut noch nicht. Ein Umweg ist, erst das Bild hochzuladen, das Objekt zu speichern dann das Bild entsprechend umzubenennen und das Objekt wiederum zu speichern:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ImageField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;tmp/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c"&gt;# Modelobjekt speichern und einen Primärschlüssel (self.pk) erhalten&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# Nur umbennenen wenn sich die Datei noch im tmp-Pfad befindet&lt;/span&gt;
        &lt;span class="n"&gt;MEDIA_ROOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abspath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MEDIA_ROOT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MEDIA_ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;tmp/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;

            &lt;span class="c"&gt;# Neuen Dateinamen mit Pfad und Primärschlüssel generieren&lt;/span&gt;
            &lt;span class="n"&gt;new_filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s%s%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MEDIA_ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;images/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;splitext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

            &lt;span class="c"&gt;# Letztendlich die Datei umbennenen und den neuen Pfad speichern&lt;/span&gt;
            &lt;span class="n"&gt;old_filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MEDIA_ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c"&gt;# Den neuen Dateinamen im Model setzen und noch einmal speichern&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_filename&lt;/span&gt;
            &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Das ganze geht bestimmt noch hübscher und enthält einige Fallstricke, so muss der Ordner &lt;code&gt;images/&lt;/code&gt; schon existieren. Aber seis drum, Bilder die so hochgeladen werden, würden als Dateinamen den Primärschlüssel des jeweiligen Modelobjekts erhalten:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bild.jpg &amp;gt; /media/images/1.jpg&lt;/li&gt;
&lt;li&gt;foobar.jpg &amp;gt; /media/images/2.jpg&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Dateinamen mit Datum und Zeit&lt;/h3&gt;
&lt;p&gt;Der Vollständigkeit halber sei noch erwähnt, dass das &lt;code&gt;upload_to&lt;/code&gt; Argument auch ohne Umwege &lt;a href="http://docs.python.org/library/time.html#time.strftime"&gt;strftime-Argumente&lt;/a&gt; verwerten kann. Ein &lt;code&gt;upload_to='/images/%Y/%m/'&lt;/code&gt; würde das Bild im Ordner &lt;code&gt;/images/2009/02/bild.jpg&lt;/code&gt; speichern.&lt;/p&gt;</description><pubDate>Sun, 08 Feb 2009 12:35:13 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/ordnung-im-medienordner-dynamische-upload-pfade.html</guid></item><item><title>Bitterkalt</title><link>http://www.freakydog.de/bitterkalt.html</link><description>&lt;p&gt;Überall in Deutschland frieren die Menschen. Überall? Nein, ein kleines Dorf am Rand der Scheibe wehrt sich gegen die Kälte.&lt;/p&gt;
&lt;img src="http://static.mahner.org/assets/weblog/wetterkalt-1.png" alt=""/&gt;

&lt;p&gt;Hier regnet es sogar. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_unhappy.png" alt="Smiley:  :-(" title=" :-(" class="smiley"/&gt;&lt;/p&gt;</description><pubDate>Fri, 09 Jan 2009 10:16:23 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/bitterkalt.html</guid></item><item><title>Spass mit Newforms-Admin - Ganz schnell Widgets zuweisen </title><link>http://www.freakydog.de/spass-mit-newforms-admin-ganz-schnell-widgets-zuweisen.html</link><description>&lt;p&gt;In den vergangenen &lt;em&gt;Spass mit Newforms-Admin&lt;/em&gt; Beiträgen hab ich immer wieder gezeigt, wie man das Widget (die HTML-Ausgabe) eines Feldes beeinflusst und ändert.&lt;/p&gt;
&lt;p&gt;Als Beispiel nochmal ein simples Model, bei dem letztendlich der Titel in großer Schrift ausgegeben werden soll.&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foobar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blank&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Also basteln wir uns &amp;ndash; wie gehabt &amp;ndash; eine ModelAdmin-Klasse und darin eine Methode &lt;em&gt;formfield_for_dbfield&lt;/em&gt;. Diese durchläuft alle Felder und wenn sie auf den passenden Titel trifft, ändert sie das Widget dazu.&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils.safestring&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mark_safe&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Foobar&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BigTitleWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;widgets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;lt;input name=&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; value=&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; style=&amp;quot;font-size:1.5em&amp;quot;/&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mark_safe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FoobarAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;formfield_for_dbfield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FoobarAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;formfield_for_dbfield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;widget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BigTitleWidget&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;

&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Foobar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FoobarAdmin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Aber ist das nicht super hässlich? Irgendwann bin ich mal über einen Codeblock von &lt;a href="http://www.jannisleidel.com/2008/11/autocomplete-widget-for-django-tagging-form-fields/"&gt;Jannis&lt;/a&gt; gestolpert und hab gesehen, dass die ModelAdmin-Klasse ein Attribut &lt;em&gt;form&lt;/em&gt; besitzt, dass das automatisch generierte &lt;a href="http://docs.djangoproject.com/en/dev/topics/forms/modelforms/?from=olddocs"&gt;ModelForm&lt;/a&gt;  mit einem eigenen überschreiben kann. Hey, und das ist sogar &lt;a href="http://docs.djangoproject.com/en/dev/ref/contrib/admin/#id1"&gt;dokumentiert&lt;/a&gt;.&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils.safestring&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mark_safe&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Foobar&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BigTitleWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;widgets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;lt;input name=&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; value=&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; style=&amp;quot;font-size:1.5em&amp;quot;/&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mark_safe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FoobarForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelForm&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;BigTitleWidget&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FoobarAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FoobarForm&lt;/span&gt;

&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Foobar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FoobarAdmin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Das ist doch viel schöner! &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :-)" title=" :-)" class="smiley"/&gt;&lt;/p&gt;
&lt;p&gt;Also nie wieder &lt;em&gt;formfield_for_dbfield&lt;/em&gt;? Nicht ganz, denn innerhalb des ModelForms hat man keinen Zugriff auf die anderen Parameter des aktuellen Admin-Views. Will man dem Widget weitere Parameter übergeben (wie z.B. die aktuelle User-ID, &lt;a href="http://www.mahner.org/weblog/spass-mit-newforms-admin-automatische-felder/"&gt;hier erklärt&lt;/a&gt;, führt der Weg weiterhin über &lt;em&gt;formfield_for_dbfield&lt;/em&gt;.&lt;/p&gt;</description><pubDate>Wed, 31 Dec 2008 00:44:47 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/spass-mit-newforms-admin-ganz-schnell-widgets-zuweisen.html</guid></item><item><title>Unter der Lupe: Template-Context-Prozessoren</title><link>http://www.freakydog.de/unter-der-lupe-template-context-prozessoren.html</link><description>&lt;p&gt;Djangos Templates stehen zwei unterschiedliche Arten zur Verfügung, wie Variablen ins Template gelangen (Template-Filter und -Tags jetzt außen vorgelassen). Die lokalen Variablen, die pro View festgelegt werden und die Template-Context-Prozessoren (im folgenden &lt;em&gt;TCP&lt;/em&gt; genannt), jeweils ein Dictionary mit Variablen, das &lt;strong&gt;allen&lt;/strong&gt; Templates zur Verfügung steht.&lt;/p&gt;
&lt;p&gt;Einige TCPs sind in der Grundkonfiguration eines Django-Projekts schon mit dabei und bestimmt hast du sie schon einmal gesehen:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;TEMPLATE_CONTEXT_PROCESSORS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.core.context_processors.auth&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.core.context_processors.i18n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.core.context_processors.media&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Hinweis:&lt;/strong&gt; Wenn du ein neues Projekt mittels &lt;code&gt;django-admin.py startproject foo&lt;/code&gt; erstellt hast, ist dieser Eintrag &lt;em&gt;nicht&lt;/em&gt; in der &lt;code&gt;settings.py&lt;/code&gt; vorhanden. Er steht aber in den Grundeinstellungen &lt;code&gt;django.conf.global_settings&lt;/code&gt;. Wenn du etwas ändern willst, packe den oberen Schnipsel ans Ende deiner &lt;code&gt;settings.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Diese Tupel wird in der Einstellung &lt;code&gt;settings.TEMPLATE_CONTEXT_PROCESSORS&lt;/code&gt; festgelegt. Am bekanntesten wird wohl der media-TCP sein, er stellt allen Templates die Variable &lt;code&gt;MEDIA_URL&lt;/code&gt; zur Verfügung, ein Link zum Ordner mit den statischen Mediendateien.&lt;/p&gt;
&lt;p&gt;TCPs können natürlich auch selbst erstellt werden, die Syntax ist sehr einfach:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mein_prozessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;&amp;#39;VARIABLEN_NAME&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;wert&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Es ist eine einfache Funktion die als einziges Argument die aktuelle request-Variable nimmt und ein Dictionary zurückgibt. Eingebunden wird sie in die &lt;code&gt;settings.py&lt;/code&gt; wie oben schon beschrieben: &lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5
6&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;TEMPLATE_CONTEXT_PROCESSORS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.core.context_processors.auth&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.core.context_processors.i18n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.core.context_processors.media&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;mein_app.context_processors.mein_prozessor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Es ist eine gute Idee, die Datei die diese Funktion beinhaltet, auch 'context_processors.py' zu nennen.&lt;/p&gt;
&lt;h3&gt;Nochmal im Vergleich&lt;/h3&gt;
&lt;p&gt;Da sich mit Quellcode eine Sache am besten verstehen lässt, hier noch einmal ein einfacher View:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5
6
7&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;startseite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render_to_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;&amp;#39;pfad/zum/template.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;headline&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Meine Startseite&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="s"&gt;&amp;#39;text&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Lorem ipsum dolor bla.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Die hier benutzte &lt;code&gt;render_to_response&lt;/code&gt; Funktion gibt das Template (erstes Argument) zurück mit den &lt;strong&gt;lokalen Variablen&lt;/strong&gt; (zweites Argument) und stellt auch alle globalen TCPs bereit (drittes Argument). In einer flauschigen Grafik schaut das so aus:&lt;/p&gt;
&lt;img src="http://static.mahner.org/assets/weblog/django/tcp1.png" alt=""/&gt;

&lt;h3&gt;Was nutzt man wann?&lt;/h3&gt;
&lt;p&gt;Wann man TCPs nutzt dürfte nun klar sein. Soll ein Wert &lt;strong&gt;jedem&lt;/strong&gt; Template zur Verfügung stehen, baut man dafür einen TCP. Ein Beispiel ist auf dieser Seite meine Fußleiste, dort sind auf jeder Seite die neuesten Blogbeiträge und Bookmarks zu sehen. &lt;/p&gt;
&lt;h3&gt;Der FAQ-Teil&lt;/h3&gt;
&lt;p&gt;Nun bist du vielleicht auf diese Seite gelangt, weil man dich im Django-Channel hierher verwiesen hat. Jetzt kommen die häufigsten Fehler im Umgang mit Template-Context-Prozessoren (TCP).&lt;/p&gt;
&lt;p&gt;Eine typisches Problem dürfte zum Beispiel sein: &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ich kann in meinem Template die &lt;code&gt;MEDIA_URL&lt;/code&gt; Variable nicht nutzen, sie gibt nur einen leeren String zurück.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Mögliche Fehlerquellen&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. Groß/Kleinschreibung&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ganz simpel eigentlich. In Django-Templates werden Variablen zwischen Groß- und Kleinschreibung unterschieden. &lt;code&gt;Media_URL&lt;/code&gt; ist also nicht das selbe wie &lt;code&gt;MEDIA_URL&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Du hast einen eigenen View geschrieben der render_to_response nutzt&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Der häufigste Fehler ist, dass du &lt;code&gt;render_to_response&lt;/code&gt; vergessen hast, als &lt;strong&gt;drittes Argument&lt;/strong&gt; den Request-Context zur Verfügung zu stellen. Schau nochmal auf die Beispielfunktion von oben:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5
6
7
8&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.template.context&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RequestContext&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;startseite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render_to_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;&amp;#39;pfad/zum/template.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;headline&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Meine Startseite&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="s"&gt;&amp;#39;text&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Lorem ipsum dolor bla.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;&lt;code&gt;RequestContext&lt;/code&gt; analysiert im Prinzip die Module aus &lt;code&gt;settings.TEMPLATE_CONTEXT_PROCESSORS&lt;/code&gt; und stellt sie dem Template zur Verfügung.&lt;/p&gt;
&lt;p&gt;Djangos Generic-Views machen das automatisch! Warum &lt;code&gt;render_to_repsonse&lt;/code&gt; nicht? Im Prinzip geht es darum, dass du die Möglichkeit &lt;em&gt;hast&lt;/em&gt;, diese Variablen nicht dem Template zur Verfügung zu stellen. Klingt komisch, ist aber so. Oft ist dies der Fall, wenn Dritte Zugriff auf die Templates haben und nicht alle globalen Variablen wissen sollen.&lt;/p&gt;
&lt;p&gt;Das ist aber nicht DRY *kreisch*. Es gibt Mittel und Wege das semi-automatisch zu machen. Auf &lt;a href="http://fi.am/entry/shortcutting-render_to_response/"&gt;dieser Seite&lt;/a&gt; sind einige Shortcuts erklärt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Du willst auf den &lt;em&gt;request&lt;/em&gt; TCP zugreifen&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Bis vor Django 1.0 war der TCP &lt;code&gt;django.core.context_processors.request&lt;/code&gt; noch Teil der Grundeinstellungen. Dieser wurde nun (aus Sicherheits- oder Performancegründen, ich weiß es nicht) entfernt. Viele 3rd-Party-Apps nutzen diesen TCP ohne zu dokumentieren, wie er eingebunden wird.&lt;/p&gt;
&lt;p&gt;Wenn du in deiner &lt;code&gt;settings.py&lt;/code&gt; noch keine Definition für &lt;code&gt;TEMPLATE_CONTEXT_PROCESSORS&lt;/code&gt; hast, packe diesen Schnipsel ans Ender der Datei:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5
6&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;TEMPLATE_CONTEXT_PROCESSORS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.core.context_processors.auth&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.core.context_processors.i18n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.core.context_processors.media&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.core.context_processors.request&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Alternativ, wenn du schon eine Prozessoren-Liste hast, packe einfach &lt;code&gt;django.core.context_processors.request&lt;/code&gt; dazu.&lt;/p&gt;
&lt;p&gt;Das wars, eine erschöpfende Antwort auf die #1 FAQ im Django IRC-Channel. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :-)" title=" :-)" class="smiley"/&gt;&lt;/p&gt;</description><pubDate>Sun, 21 Dec 2008 21:11:10 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/unter-der-lupe-template-context-prozessoren.html</guid></item><item><title>Request-unabhängige Locale-Einstellungen </title><link>http://www.freakydog.de/request-unabhangige-locale-einstellungen.html</link><description>&lt;p&gt;Manchmal ist es nötig, in einem Request/View verschiedene Locale-Einstellungen zu nutzen. Der häufigste Fall dürfte der Versand von E-Mails an verschiedene User sein, die jeweils die Nachricht in ihrer Sprache erhalten sollen.&lt;/p&gt;
&lt;p&gt;Man könnte einfacherweise für diese E-Mail für jede Sprache ein eigenes Template anlegen, aber wozu gibt es die gettext-Unterstütung. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :)" title=" :)" class="smiley"/&gt; &lt;/p&gt;
&lt;p&gt;Djangos I18N-Kern bringt eine Funktion &lt;code&gt;django.utils.translation.activate&lt;/code&gt; mit, mit der man zur Laufzeit die Locale-Einstellung ändern kann.&lt;/p&gt;
&lt;p&gt;Als Beispiel dient ein einfaches Template mit einem Translation-String.&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;spaceless&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;load&lt;/span&gt; &lt;span class="nv"&gt;i18n&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;trans&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Thanks for using our site!&amp;quot;&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endspaceless&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Und eine Dummy-Funktion. Dabei wird über die Sprachen iteriert (normalerweise würde die jeweilige Spracheinstellung im Userprofil gespeichert sein) und das &lt;code&gt;email.txt&lt;/code&gt; Template mit der jeweiligen Sprache im Terminal ausgegeben:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.template.loader&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;render_to_string&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils.translation&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;activate&lt;/span&gt;

&lt;span class="n"&gt;current_language&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_language&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;languages_to_render&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;en-us&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;de-de&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;pt-br&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;fr&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;languages_to_render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;render_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;email.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_language&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Ausgabe:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;Thanks for using our site!
Vielen Dank, dass Sie unsere Seite benutzen!
Obrigado por usar nosso site!
Merci d&amp;#39;utiliser notre site !
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Einfach oder? Beachten sollte man noch, dass man den originalen Locale-Wert vor dem ändern speichert (&lt;code&gt;django.utils.translation.get_language&lt;/code&gt; liefert den aktuellen Wert) und am Ende wieder aktiviert. Sonst würde in diesem Fall der Rest des Views mit der französischen Locale weiterlaufen.&lt;/p&gt;</description><pubDate>Sat, 20 Dec 2008 23:57:08 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/request-unabhangige-locale-einstellungen.html</guid></item><item><title>Hackfleischpizza</title><link>http://www.freakydog.de/hackfleischpizza.html</link><description>&lt;p&gt;Wohoo, endlich wieder Foodcontent. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :)" title=" :)" class="smiley"/&gt;&lt;/p&gt;
&lt;p&gt;Heute: Wir basteln uns eine Hackfleischpizza. Ok, dass ist nun nicht soo was besonderes aber sei's drum, es ist lecker und schnell gemacht. Wir brauchen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;einen Pizzateig&lt;/li&gt;
&lt;li&gt;300g Rinderhack&lt;/li&gt;
&lt;li&gt;eine Mozarellakugel (nennt man das so?)&lt;/li&gt;
&lt;li&gt;eine Hand voll Mais&lt;/li&gt;
&lt;li&gt;eine Zwiebel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Das Hack wird in der Pfanne erst einmal angebraten, aber nicht ganz durch. Danach von der Pfanne erst einmal auf ein Küchentuch legen damit das überflüssige Öl abläuft, sonst schwimmt die Pizza später. Der Mozarella wird grob gewürfelt und die Zwiebel in Ringe geschnitten. Alles zusammen mit einer Handvoll Mais (hier aus der Dose) auf der Pizza verteilen und für ca. 20 Minuten bei 180°C in den Ofen.&lt;/p&gt;
&lt;img src="http://static.mahner.org/assets/weblog/pizza3.jpg" alt=""/&gt;

&lt;p&gt;&lt;strong&gt;Lääääcka!&lt;/strong&gt; Dazu gibt es einen &lt;del&gt;weichen 1975'er Spätburgunder&lt;/del&gt; am Besten ein Bier nach Wahl. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_tongue.png" alt="Smiley:  :-P" title=" :-P" class="smiley"/&gt;&lt;/p&gt;
&lt;img src="http://static.mahner.org/assets/weblog/pizza4.jpg" alt=""/&gt;

&lt;img src="http://static.mahner.org/assets/weblog/pizza1.jpg" alt=""/&gt;

&lt;h3&gt;Noch ein paar Gedanken dazu:&lt;/h3&gt;
&lt;p&gt;Wer kann, macht sich den Pizzateig natürlich selber. Allerdings sagt mir mein Hobbykochherz, dass ein Hefeteig nicht so einfach herzustellen ist und schnell geht es schon gar nicht (der sollte über Nacht im Kühlschrank noch gehen).&lt;/p&gt;
&lt;p&gt;Ich für meinen Fall kaufe immer die fertigen Pizzateige inkl. Tomatensauce. Dabei sollte man nicht auf den Cent gucken. Die billigsten Fertigteige der Discounter schmecken &lt;em&gt;mir&lt;/em&gt; nicht, sind dröge und die Sauce ist völlig überwürzt.&lt;/p&gt;
&lt;p&gt;Der Fertigteig von Knack&amp;amp;Back für knapp 2€ war jedenfalls lecker. Ich hab aber nur nach ihm gegriffen, weil mir das Männchen auf der Packung gefallen hat. Ja, ich bin der Traum des Einzelhandels. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_grin.png" alt="Smiley:  :D" title=" :D" class="smiley"/&gt;&lt;/p&gt;
&lt;img src="http://static.mahner.org/assets/weblog/pizza2.jpg" alt=""/&gt;

&lt;p&gt;Und dann noch zum Belag. Leider ist es wohl eine deutsche Eigenart, eine Pizza mit hundert verschiedenen Belägen voll zu knallen, darüber noch eine 3cm dicke Käseschicht. Dabei ist weniger viel mehr, die Pizza ist viel leckerer je &lt;em&gt;weniger&lt;/em&gt; Zutaten dabei sind.&lt;/p&gt;</description><pubDate>Fri, 19 Dec 2008 20:22:51 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/hackfleischpizza.html</guid></item><item><title>Eine bessere settings.py</title><link>http://www.freakydog.de/eine-bessere-settingspy.html</link><description>&lt;p&gt;Bei einem frischen Django-Projekt ist so ziemlich die erste Änderung bei mir die &lt;code&gt;settings.py&lt;/code&gt;. 
Es gibt einige Sachen, die fehlen in der Grundkonfiguration &lt;strong&gt;immer&lt;/strong&gt;. So z.B. der request-Context-Prozessor, Caching-Einstellungen oder die Mailserver-Einstellungen (ich betreibe lokal keinen SMTP). Daneben natürlich noch die Sachen, die ich in &lt;a href="http://www.mahner.org/weblog/wiederverwendbare-django-projekte/"&gt;Wiederverwendbare Django-Projekte&lt;/a&gt; gepostet habe.&lt;/p&gt;
&lt;p&gt;Irgendwann habe ich mir mal eine generische &lt;code&gt;settings.py&lt;/code&gt; erstellt in der ich auf Anhieb alle, für mich nötigen, Einstellungen vorfinde und diese mit sinnvollen Werten vorgeingestellt. Bei jedem neuen Projekt überschreibe ich die &lt;code&gt;settings.py&lt;/code&gt; zuerst einmal mit dieser Variante. Vielleicht findet es ja jemand interessant. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :-)" title=" :-)" class="smiley"/&gt;&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;PROJECT_ROOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abspath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;PROJECT_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROJECT_ROOT&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# debug settings&lt;/span&gt;
&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;

&lt;span class="n"&gt;DEBUG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="n"&gt;TEMPLATE_DEBUG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DEBUG&lt;/span&gt;
&lt;span class="n"&gt;INTERNAL_IPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;TEMPLATE_STRING_IF_INVALID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;STRING_NOT_SET&amp;#39;&lt;/span&gt;

&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# cache settings&lt;/span&gt;
&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;

&lt;span class="n"&gt;CACHE_BACKEND&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;locmem://&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;CACHE_MIDDLEWARE_KEY_PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;_&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;PROJECT_NAME&lt;/span&gt;
&lt;span class="n"&gt;CACHE_MIDDLEWARE_SECONDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;600&lt;/span&gt;

&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# email and error-notify settings&lt;/span&gt;
&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;

&lt;span class="n"&gt;ADMINS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c"&gt;# (&amp;#39;Your Name&amp;#39;, &amp;#39;your_email@example.com&amp;#39;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;MANAGERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ADMINS&lt;/span&gt;

&lt;span class="n"&gt;DEFAULT_FROM_EMAIL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;from-mail@example.com&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;SERVER_EMAIL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;error-notify@example.com&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;EMAIL_SUBJECT_PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;[&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;] &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;PROJECT_NAME&lt;/span&gt;
&lt;span class="n"&gt;EMAIL_HOST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;EMAIL_PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;25&lt;/span&gt;
&lt;span class="n"&gt;EMAIL_HOST_USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;EMAIL_HOST_PASSWORD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;EMAIL_USE_TLS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# auth settings&lt;/span&gt;
&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;

&lt;span class="n"&gt;LOGIN_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/accounts/login/&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;LOGOUT_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/accounts/logout/&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;LOGIN_REDIRECT_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;

&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# database settings&lt;/span&gt;
&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;

&lt;span class="n"&gt;DATABASE_ENGINE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;sqlite3&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;DATABASE_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROJECT_ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;dev.db&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;DATABASE_USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;DATABASE_PASSWORD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;DATABASE_HOST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;DATABASE_PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;

&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# i18n and url settings&lt;/span&gt;
&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;

&lt;span class="n"&gt;TIME_ZONE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Europe/Berlin&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;LANGUAGE_CODE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;de&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;LANGUAGES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;en&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;English&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
             &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;de&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;German&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;USE_I18N&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

&lt;span class="n"&gt;SITE_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1&lt;/span&gt;

&lt;span class="n"&gt;MEDIA_ROOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROJECT_ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;site_media&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;MEDIA_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/media/&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;ADMIN_MEDIA_PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/django_admin_media/&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;ROOT_URLCONF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;.urls&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;PROJECT_NAME&lt;/span&gt;

&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# application and middleware settings&lt;/span&gt;
&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;

&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.contrib.auth&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.contrib.contenttypes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.contrib.sessions&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.contrib.sites&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.contrib.admin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.contrib.humanize&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;MIDDLEWARE_CLASSES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.contrib.sessions.middleware.SessionMiddleware&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.contrib.auth.middleware.AuthenticationMiddleware&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.middleware.http.ConditionalGetMiddleware&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="c"&gt;#    &amp;#39;django.middleware.gzip.GZipMiddleware&amp;#39;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.middleware.common.CommonMiddleware&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;TEMPLATE_CONTEXT_PROCESSORS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.core.context_processors.auth&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.core.context_processors.debug&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.core.context_processors.i18n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.core.context_processors.media&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.core.context_processors.request&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;TEMPLATE_DIRS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROJECT_ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;templates&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;TEMPLATE_LOADERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.template.loaders.filesystem.load_template_source&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;django.template.loaders.app_directories.load_template_source&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="c"&gt;#    &amp;#39;django.template.loaders.eggs.load_template_source&amp;#39;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# the secret key&lt;/span&gt;
&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;SECRET_KEY&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;NameError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;SECRET_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROJECT_ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;secret.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;SECRET_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SECRET_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;IOError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;
            &lt;span class="n"&gt;SECRET_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&amp;amp;*(-_=+)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
            &lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SECRET_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;IOError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Please create a &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; file with random characters to generate your secret key!&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;SECRET_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# third party&lt;/span&gt;
&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;

&lt;span class="c"&gt;# ..third party app settings here&lt;/span&gt;

&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# host specific settings&lt;/span&gt;
&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;local_settings&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</description><pubDate>Sun, 14 Dec 2008 18:16:47 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/eine-bessere-settingspy.html</guid></item><item><title>Git-Branch im Bash-Prompt anzeigen</title><link>http://www.freakydog.de/git-branch-im-bash-prompt-anzeigen.html</link><description>&lt;p&gt;Wer mit &lt;a href="http://git.or.cz/"&gt;Git&lt;/a&gt; arbeitet und öfter mal Dateien im falschen Branch ändert &amp;ndash; wie ich &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_wink.png" alt="Smiley:  ;)" title=" ;)" class="smiley"/&gt; &amp;ndash; dem hilft vielleicht, den aktuellen Branch im Command-Prompt anzuzeigen. Das ganze schaut bei mir so aus:&lt;/p&gt;
&lt;img src="http://static.mahner.org/assets/weblog/gitprompt.png" alt=""/&gt;

&lt;p&gt;Der passende Eintrag für deine &lt;code&gt;.profile&lt;/code&gt; oder &lt;code&gt;.bashrc&lt;/code&gt; Datei lautet: &lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PS1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;\[\033[01;32m\]\u\[\033[01;34m\] \w\[\033[31m\]$(__git_ps1 &amp;quot; (%s)&amp;quot;)\[\033[01;34m\]$\[\033[00m\] &amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Den Zauber macht die Kette &lt;code&gt;$(__git_ps1 " (%s)")&lt;/code&gt; möglich. &lt;code&gt;__git_ps1&lt;/code&gt; ist ein Befehl aus dem &lt;em&gt;contrib/completion&lt;/em&gt; Verzeichnis der Git-Source-Distribution der den aktuellen Branch des Verzeichnisses ausgibt. Der wird aber hier unter Ubuntu mit dem Paket direkt mitinstalliert.&lt;/p&gt;
&lt;p&gt;Wer OSX nutzt und Git via Macports installiert hat, muss wahrscheinlich die Datei &lt;code&gt;/opt/local/etc/bashcompletion.d/git&lt;/code&gt; sourcen. &lt;a href="http://zerokspot.com/weblog/2008/12/04/git-branches-and-ps1/"&gt;Danke Horst für die Info&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Und wer noch mehr wissen will, findet bei &lt;a href="http://asemanfar.com/Current-Git-Branch-in-Bash-Prompt"&gt;asemanfar.com&lt;/a&gt; weitere Promt-Varianten.&lt;/p&gt;</description><pubDate>Thu, 04 Dec 2008 14:57:13 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/git-branch-im-bash-prompt-anzeigen.html</guid></item><item><title>Durchwahl</title><link>http://www.freakydog.de/durchwahl.html</link><description>&lt;p&gt;Ich habe eine Telefonnummer, eine Durchwahlnummer für die Arbeit bei mir zu Hause. Also nicht die Bürodurchwahl sondern noch geheimer, die Nummer landet ja privat hier. Diese Nummer ist nur meiner Firma und einer Hand voll Kunden bekannt. Und diese Nummer nutze ich so selten, dass ich sie nicht mal aus dem Kopf weiß sondern immer in der FritzBox nachgucken muss. Im Telefonbuch oder so steht sie natürlich auch nicht.&lt;/p&gt;
&lt;p&gt;Eben diese Nummer wird nun schon seit 2 Wochen permanent von Outbound-Callcentern belagert. Wo haben die diese Nummer her? Ich fürchte es hat etwas &lt;a href="http://www.heise.de/security/Alter-Raub-neuer-Skandal-17-Millionen-Telekom-Nummern-entwendet--/news/meldung/116913"&gt;damit&lt;/a&gt; oder einem noch unbekannten Fall zu tun... &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_unhappy.png" alt="Smiley:  :-(" title=" :-(" class="smiley"/&gt;&lt;/p&gt;</description><pubDate>Tue, 04 Nov 2008 13:13:11 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/durchwahl.html</guid></item><item><title>Autsch</title><link>http://www.freakydog.de/autsch.html</link><description>&lt;p&gt;Es gibt nur eine Sache, die noch schlimmer ist als ein Legostein, auf den man morgens tritt:&lt;/p&gt;
&lt;img src="http://static.mahner.org/assets/weblog/bierdeckel.jpg" alt=""/&gt;</description><pubDate>Sat, 01 Nov 2008 14:26:02 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/autsch.html</guid></item><item><title>Der Mythos vom statischen Servieren</title><link>http://www.freakydog.de/der-mythos-vom-statischen-servieren.html</link><description>&lt;p&gt;In Djangos Dokumentation wird empfohlen, statische Dateien durch den Webserver
ausliefern zu lassen. Was bedeutet das? Django läuft doch auf dem Webserver.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Richtig" src="http://static.mahner.org/assets/weblog/django/static_ok.png" /&gt;&lt;/p&gt;
&lt;p&gt;Im Prinzip geht es darum, Dateien die Django nicht bearbeiten muss, nicht durch
den Django-Prozess zu schleifen. Statische Dateien sollen also den direkten Weg
von der Festplatte zum Webserver finden (grüne Linie).&lt;/p&gt;
&lt;p&gt;Das &amp;ndash; ich nenne es mal &amp;ndash; Problem ist, dass Django eine Funktion bereit stellt,
statische Dateien durch Django zu schleifen: &lt;a href="http://docs.djangoproject.com/en/dev/howto/static-files/"&gt;How to serve static files&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Falsch" src="http://static.mahner.org/assets/weblog/django/static_static.png" /&gt;&lt;/p&gt;
&lt;p&gt;Hierbei wandern alle statischen Dateien durch den Django-Prozess
anstatt dass sie direkt an den Webserver geliefert werden. Das ist ganz klar
ineffizient.&lt;/p&gt;
&lt;p&gt;Warum gibt's das dann? In erster Linie ist es für Entwickler die den
&lt;a href="http://docs.djangoproject.com/en/dev/ref/django-admin/#runserver-optional-port-number-or-ipaddr-port"&gt;Devserver&lt;/a&gt;
nutzen gedacht und &lt;strong&gt;während der Entwicklung&lt;/strong&gt; keinen zusätzlichen Webserver zum Static-Serving
aufsetzen möchten.&lt;/p&gt;
&lt;p&gt;Fazit also: Vermeide Static-Serving durch Django um jeden Preis &amp;ndash; es gibt natürlich
Ausnahmen, siehe weiter unten.&lt;/p&gt;
&lt;h3&gt;PHP macht das doch auch so&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Letzens hatte mich ein PHP-Entwickerl auch gefragt, wieso er da nicht Django
nehmen kann, dass geht doch auch mit PHP.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Oberes ist ein Zitat aus dem &lt;a href="http://www.django-de.org/"&gt;#django-de&lt;/a&gt; Channel.
PHP macht es aber (unbewusst) genauso wie Django. Statische Dateien werden auch
direkt vom Webserver geliefert und nicht erst durch den PHP-Prozessor geschleift.
Dieser verarbeitet im Normalfall nur Dateien mit der Endung &lt;em&gt;.php&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Ein Static-Serving &lt;em&gt;durch&lt;/em&gt; PHP würde etwa so aussehen, dass der Webserver ein
Stylesheet mit 'GET /stylesheet.php' anfordert, und diese Datei das unten gezeigte
Script ausführt.&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;file_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/var/www/static/stylesheet.css&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Ziemlich dämlich, oder? Aber leider alles schon gesehen. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_unhappy.png" alt="Smiley:  :-(" title=" :-(" class="smiley"/&gt;&lt;/p&gt;
&lt;h3&gt;Spezialisten&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Überlasse das Static-Serving einem Server der besser dafür geeignet ist, zum
Beispiel nginx oder Lighty.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Den oberen Satz hört man sehr häufig wenn es es Static-Serving geht. Was ist gemeint?&lt;/p&gt;
&lt;p&gt;Zuerst einmal, &lt;a href="http://nginx.net/"&gt;nginx&lt;/a&gt; und &lt;a href="http://www.lighttpd.net/"&gt;lighttpd&lt;/a&gt;, oder
auch liebevoll &lt;em&gt;lighty&lt;/em&gt; genannt, sind Webserver, die auf das Ausliefern statischer Dateien optimiert
sind. Dabei sind sie nicht unbedingt schneller als der Apache, ihre Stärke liegt
in der Auslieferung von mehreren tausend Dateien &lt;strong&gt;pro Sekunde&lt;/strong&gt;, denn dabei sind sie
weniger RAM- und CPU-hungrig und skalieren besser.&lt;/p&gt;
&lt;p&gt;Es gibt einen Weg, das beste beiden Webservern (Apache für Django, nginx
für statische Dateien) herauszuholen. Dabei übernimmt nginx (oder lighty) als &lt;strong&gt;Frontend-Server&lt;/strong&gt;
alle Anfragen des Webbrowsers und serviert die statischen Dateien direkt. Anfragen
auf dynamische Dateien werden an den Apache-Prozess weitergeleitet, der auf einem anderen
Port (also nicht :80) lauscht. (&lt;a href="http://superjared.com/projects/static-generator/#sample_nginx_configuration"&gt;Konfigurationsbeispiel&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;img alt="Sieht cool aus" src="http://static.mahner.org/assets/weblog/django/static_nginx.png" /&gt;&lt;/p&gt;
&lt;p&gt;Das Ganze ist dann schon ein recht kompliziertes Setup. Meine persönliche Meinung ist,
wenn man den Punkt erreicht hat, dass ein spezialisierter Webserver für statische
Dateien nötig ist, sollte man den statischen Kram auf eine eigene Maschine auslagern:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Made for suits" src="http://static.mahner.org/assets/weblog/django/static_nginx_double.png" /&gt;&lt;/p&gt;
&lt;p&gt;Letztendlich ist alles eine Frage des Budgets. Nicht unerwähnt soll bleiben, dass
es auch Firmen gibt, die sich auf das Static-Serving spezialisiert haben. &lt;a href="http://www.akamai.de/"&gt;Akamai&lt;/a&gt;
ist ein Vertreter aber auch &lt;a href="http://aws.amazon.com/s3/"&gt;Amazons S3 Service&lt;/a&gt; ist
hervorragend dafür geeignet.&lt;/p&gt;
&lt;h3&gt;Was wäre wenn?&lt;/h3&gt;
&lt;p&gt;Es gibt natürlich auch das Problem, dass man statische Dateien durch Django
schleifen muss. Beispiele:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Die Datei soll nur für bestimmte, authentifizierte User verfügbar gemacht werden.&lt;br/&gt;&lt;br/&gt;
   Django stellt für solche Fälle eine Möglichkeit zur Verfügung, dass sich der Apache
   an Djangos Auth-Backend koppeln kann. Mehr Infos dazu in der
   &lt;a href="http://docs.djangoproject.com/en/dev/howto/apache-auth/#authenticating-against-django-s-user-database-from-apache"&gt;Dokumentation&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Die Datei wird dynamisch erzeugt, zum Beispiel ein PDF-Export der aktuellen Seite.&lt;br/&gt;&lt;br/&gt;
   Vielleicht wäre hier eine Lösung, die Datei im Medienordner zu speichern und
   einen Location-Header zu dieser Datei zu senden. Dann übernimmt der Webserver
   wieder das Ausliefern der statischen Dateien und man erhält zudem ein statisches
   Caching der Dateien.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Jedenfalls, wenn es denn nicht anders möglich ist versuche große Dateien als
&lt;a href="http://docs.djangoproject.com/en/dev/ref/request-response/#passing-iterators"&gt;Iterator&lt;/a&gt;
an das Response-Objekt zu übergeben. Damit wird nicht die gesamte Datei in den
Prozess geladen (bei einer 80MB Datei wäre der wsgi-Prozess dann
90MB+ groß) sondern in kleinen Häppchen (Chunks) durchgeschleift.&lt;/p&gt;
&lt;p&gt;Beachte dazu aber, dass die GZip-Middleware diesen Prozess zerstören kann: Ticket
&lt;a href="http://code.djangoproject.com/ticket/7581"&gt;#7581&lt;/a&gt;. Aber auch dafür gibt es eine
einfache, performante &lt;a href="http://www.django-hosting.de/wiki/ApacheModDeflate/"&gt;Lösung&lt;/a&gt;. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :-)" title=" :-)" class="smiley"/&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Noch ein wichtiger Nachtrag:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Auch wenn in den Bildern mod_python angegeben ist, empfehle ich euch doch
ganz stark &lt;a href="http://code.google.com/p/modwsgi/"&gt;mod_wsgi&lt;/a&gt; als Kleber zwischen
Django und Apache. Es ist resoucen schonender, performanter und allgemein sympathischer. Leider aber hat mod_wsgi kein Logo das ich benutzen konnte. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_wink.png" alt="Smiley:  ;-)" title=" ;-)" class="smiley"/&gt;&lt;/p&gt;
&lt;h3&gt;Mehr dazu&lt;/h3&gt;
&lt;p&gt;Mehr Lesestoff und vor allem Konfigurations-Beispiele für verschiedene Webserver-Szenarien
findest du im deutschsprachigen &lt;a href="http://www.django-hosting.de/wiki/WikiIndex/"&gt;django-hosting Wiki&lt;/a&gt;.&lt;/p&gt;</description><pubDate>Fri, 10 Oct 2008 12:21:19 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/der-mythos-vom-statischen-servieren.html</guid></item><item><title>Kabelsalat</title><link>http://www.freakydog.de/kabelsalat.html</link><description>&lt;p&gt;Aufgrund interner Restrukturierungsmaßnahmen (aka Möbelrücken) war ich gezwungen,
mein jahrlang, mühsam aufgebautes Kommunikationsnetzwerk neu zu ordnen:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vorher:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="lightbox" href="http://static.mahner.org/assets/weblog/kabelsalat_vorher.jpg"&gt;
&lt;img src="http://static.mahner.org/assets/weblog/kabelsalat_vorher_klein" alt=""/&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nachher:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="lightbox" href="http://static.mahner.org/assets/weblog/kabelsalat_nachher.jpg"&gt;
&lt;img src="http://static.mahner.org/assets/weblog/kabelsalat_nachher_klein" alt=""/&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Übrig geblieben ist eine alte Fritzbox, die ich aber zur Sicherheit behalten werde,
falls die jetzige mal Ihren Geist aufgibt. Daneben ein Fon-Wlan-Point den ich
aufgrund der derzeitigen deutschen Rechtslage (Mitstöhrerhaftung, Vorratsdatenspeicherung)
schon seit längerer Zeit nicht mehr in Betrieb hatte. Aber da hat sich wohl
&lt;a href="http://www.heise.de/newsticker/Bundesregierung-Keine-Vorratsdatenspeicherung-fuer-kleine-Hotspot-Betreiber--/meldung/116556"&gt;was geändert&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Und dann noch mein &lt;em&gt;Draytek We2200&lt;/em&gt;, ein klasse Router mit Wlan, VPN und jedem
Quatsch den sich ein Netzwerkadmin wünschen kann. Ich hatte gehofft, ihn zu
einem fairen Preis noch bei eBay verhökern zu können. Dort wird er aber um
die 4 Euro gehandelt, Neupreis damals 300,- DM. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_unhappy.png" alt="Smiley:  :-(" title=" :-(" class="smiley"/&gt; &lt;/p&gt;
&lt;p&gt;Ist ja auch klar, das WLan unterstützt nur WEP-Verschlüsselung und ist damit
praktisch unbrauchbar. Leider ist es der beste WLan-Router den ich je hatte, durch
seine 2 anständigen, großen Antennen erreicht es beste Ausleuchtung. &lt;/p&gt;
&lt;p&gt;Dann bleibt er eben auch bei mir, gammelt erstmal im Keller. Wer weiß wofür ich ihn
nochmal brauche. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :-)" title=" :-)" class="smiley"/&gt;&lt;/p&gt;
&lt;p&gt;Achja, und ein Hoch auf farbige Netzwerkkabel. Die beste Erfindung seit der Spülmaschine.&lt;/p&gt;</description><pubDate>Tue, 07 Oct 2008 11:04:52 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/kabelsalat.html</guid></item><item><title>Forms: Dynamisch Felder hinzufügen</title><link>http://www.freakydog.de/forms-dynamisch-felder-hinzufugen.html</link><description>&lt;p&gt;Kurzer und schmerzloser Tipp für zwischendurch: Wie kann man in einem
&lt;del&gt;New&lt;/del&gt;Forms-Formular dynamisch Felder hinzufügen?&lt;/p&gt;
&lt;h3&gt;Abstraktion&lt;/h3&gt;
&lt;p&gt;Am einfachsten per Abstraktion. Das Formular mit den weiteren Feldern leitet
sich einfach vom Basisformular ab:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Userform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;passwort&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PasswordInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;render_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserformKontaktdaten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Userform&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c"&gt;#                      ^ Abgeleitet von Userform&lt;/span&gt;
    &lt;span class="n"&gt;vorname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;nachname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Ausgabe:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Userform&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_ul&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;li&amp;gt;&amp;lt;label for=&amp;quot;id_username&amp;quot;&amp;gt;Username:&amp;lt;/label&amp;gt; &amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;username&amp;quot; id=&amp;quot;id_username&amp;quot; /&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;li&amp;gt;&amp;lt;label for=&amp;quot;id_passwort&amp;quot;&amp;gt;Passwort:&amp;lt;/label&amp;gt; &amp;lt;input type=&amp;quot;password&amp;quot; name=&amp;quot;passwort&amp;quot; id=&amp;quot;id_passwort&amp;quot; /&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UserformKontaktdaten&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_ul&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;li&amp;gt;&amp;lt;label for=&amp;quot;id_username&amp;quot;&amp;gt;Username:&amp;lt;/label&amp;gt; &amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;username&amp;quot; id=&amp;quot;id_username&amp;quot; /&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;li&amp;gt;&amp;lt;label for=&amp;quot;id_passwort&amp;quot;&amp;gt;Passwort:&amp;lt;/label&amp;gt; &amp;lt;input type=&amp;quot;password&amp;quot; name=&amp;quot;passwort&amp;quot; id=&amp;quot;id_passwort&amp;quot; /&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;li&amp;gt;&amp;lt;label for=&amp;quot;id_vorname&amp;quot;&amp;gt;Vorname:&amp;lt;/label&amp;gt; &amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;vorname&amp;quot; id=&amp;quot;id_vorname&amp;quot; /&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;li&amp;gt;&amp;lt;label for=&amp;quot;id_nachname&amp;quot;&amp;gt;Nachname:&amp;lt;/label&amp;gt; &amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;nachname&amp;quot; id=&amp;quot;id_nachname&amp;quot; /&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;h3&gt;Laufzeitabhängig&lt;/h3&gt;
&lt;p&gt;Möchte man die Felder aber schon während der Formulargenerierung dynamisch hinzufügen, 
überschreibt man einfacherweise den Konstruktor und legt die fügt die neuen Felder
zu den bestehenden hinzu:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Userform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;passwort&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PasswordInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;render_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kontaktdaten&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Userform&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# Hier dynamisch Felder hinzufuegen&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;kontaktdaten&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;vorname&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;nachname&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Ausgabe:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Userform&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_ul&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;li&amp;gt;&amp;lt;label for=&amp;quot;id_username&amp;quot;&amp;gt;Username:&amp;lt;/label&amp;gt; &amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;username&amp;quot; id=&amp;quot;id_username&amp;quot; /&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;li&amp;gt;&amp;lt;label for=&amp;quot;id_passwort&amp;quot;&amp;gt;Passwort:&amp;lt;/label&amp;gt; &amp;lt;input type=&amp;quot;password&amp;quot; name=&amp;quot;passwort&amp;quot; id=&amp;quot;id_passwort&amp;quot; /&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Userform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kontaktdaten&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_ul&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;li&amp;gt;&amp;lt;label for=&amp;quot;id_username&amp;quot;&amp;gt;Username:&amp;lt;/label&amp;gt; &amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;username&amp;quot; id=&amp;quot;id_username&amp;quot; /&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;li&amp;gt;&amp;lt;label for=&amp;quot;id_passwort&amp;quot;&amp;gt;Passwort:&amp;lt;/label&amp;gt; &amp;lt;input type=&amp;quot;password&amp;quot; name=&amp;quot;passwort&amp;quot; id=&amp;quot;id_passwort&amp;quot; /&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;li&amp;gt;&amp;lt;label for=&amp;quot;id_vorname&amp;quot;&amp;gt;Vorname:&amp;lt;/label&amp;gt; &amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;vorname&amp;quot; id=&amp;quot;id_vorname&amp;quot; /&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;li&amp;gt;&amp;lt;label for=&amp;quot;id_nachname&amp;quot;&amp;gt;Nachname:&amp;lt;/label&amp;gt; &amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;nachname&amp;quot; id=&amp;quot;id_nachname&amp;quot; /&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</description><pubDate>Mon, 29 Sep 2008 09:41:12 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/forms-dynamisch-felder-hinzufugen.html</guid></item><item><title>Frettchen an die Macht?</title><link>http://www.freakydog.de/frettchen-die-macht.html</link><description>&lt;p&gt;Schockierend! Ist mir doch kürzlich aufgefallen, dass auf den neuen EU-Heimtierausweisen ein Frettchen neben einem Hund und einer Katze abgebildet ist. Sind Frettchen wirklichen schon so populär? Ich kenne niemanden, der ein Frettchen besitzt oder es auch nur plant. Strange...&lt;/p&gt;
&lt;p&gt;&lt;img alt="Weiße Mäuse" src="http://static.mahner.org/assets/weblog/frettchen.jpg" /&gt;&lt;/p&gt;</description><pubDate>Mon, 22 Sep 2008 18:24:35 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/frettchen-die-macht.html</guid></item><item><title>Spass mit Newforms-Admin - Read-Only Felder</title><link>http://www.freakydog.de/spass-mit-newforms-admin-read-only-felder.html</link><description>&lt;p&gt;In &lt;a href="/weblog/spass-mit-newforms-admin-automatische-felder/"&gt;Automatische Felder&lt;/a&gt;
zeigte ich letztens, wie man Felder automatisch mit einem Wert versieht. Nun geht
es wieder einen Schritt weiter: Wie kann man Felder als "Text" anzeigen, so dass
keine Eingabe des Users möglich ist.&lt;/p&gt;
&lt;h3&gt;Einfach und schnell&lt;/h3&gt;
&lt;p&gt;Das Stichwort ist: Widgets. Widgets legen fest, wie die Daten später im 
HTML ausgegeben werden &amp;ndash; und wie Widgets den Admin-Felder zugewiesen werden, habe
ich in den letzten Artikeln schon zur Genüge erklärt. &lt;/p&gt;
&lt;p&gt;HTML-Formular-Elemente besitzen schon seit gefühlten 200 Jahren ein Attribut, dass
sie "nur lesbar" macht: &lt;em&gt;disabled&lt;/em&gt;, oder XHTML-Konform: &lt;em&gt;disabled="disabled"&lt;/em&gt;.
Attribute lassen sich blitzschnell den Feldern, besser gesagt den Widgets, zuweisen:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;myproject.weblog.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Entry&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;formfield_for_dbfield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;formfield_for_dbfield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# Das Titel-Feld read-only setzen&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;disabled&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;disabled&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;&lt;img alt="Read-Only Felder" src="http://static.mahner.org/assets/weblog/django/readonly1.png" /&gt;&lt;/p&gt;
&lt;p&gt;Und fertig ist die Geschichte. Das ist mal Rapid Development. Leider &amp;ndash; wie immer &amp;ndash; 
nicht ganz, es gibt noch einen Stolperstein: Auch beim Anlegen eines neuen Eintrags
wird das Feld ausgegraut, die disabled-Geschichte darf also nur Änderungen (change) 
eines Artikels betreffen.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Read-Only Felder" src="http://static.mahner.org/assets/weblog/django/readonly2.png" /&gt;&lt;/p&gt;
&lt;p&gt;Der Methode &lt;em&gt;formfield_for_dbfield&lt;/em&gt; fehlt ein "Status-Flag", ob wir uns im Add-
oder Change-Modus befinden. Das ist aber schnell nachgeholt, die Methode &lt;em&gt;change_view&lt;/em&gt;
markiert zur Laufzeit das Admin-Objekt einfach als "_is_change" und während der 
Widget-Manipulation wird darauf geprüft.&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_is_change_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;object_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_is_change_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;change_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;object_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;formfield_for_dbfield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;formfield_for_dbfield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# Das Titel-Feld read-only setzen&lt;/span&gt;
        &lt;span class="c"&gt;# aber nur wenn wir uns im change-Modus befinden&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;title&amp;quot;&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_is_change_mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;disabled&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;disabled&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;h3&gt;Jetzt aber richtig&lt;/h3&gt;
&lt;p&gt;Ok, Problem gelöst aber schön ist was anderes. Hier im Firefox wird der Text grau
auf grau dargestellt. Die disabled-Sache kann also nicht der heilige Gral sein.
Ein anderer Ansatz ist: Zeige den Text/Inhalt des Feldes an, ohne Formularelemente.&lt;/p&gt;
&lt;p&gt;Auch hier ist es eine Widgetsache. Wir brauchen also ein Widget, dass
statt eines HTMl-Formular-Elements einfach nur den Inhalt ausspuckt.
Eigentlich ganz einfach:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils.safestring&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mark_safe&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;myproject.weblog.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Entry&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReadOnlyWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HiddenInput&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;append_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;append_text&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ReadOnlyWidget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;field_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ReadOnlyWidget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mark_safe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; &amp;lt;strong&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/strong&amp;gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append_text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_is_change_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;object_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_is_change_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;object_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;change_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;object_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;formfield_for_dbfield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;formfield_for_dbfield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# Dem Titel-Feld das read-only Widget zuweisen&lt;/span&gt;
        &lt;span class="c"&gt;# aber nur wenn wir uns im change-Modus befinden&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;title&amp;quot;&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_is_change_mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;widget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ReadOnlyWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;append_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;&lt;img alt="Read-Only Felder" src="http://static.mahner.org/assets/weblog/django/readonly3.png" /&gt;&lt;/p&gt;
&lt;p&gt;Was passiert da? Das Titel-Feld, ein Input-Textfeld, wird in ein HiddenInput
umgewandelt, im Quellcode ist es also als &lt;code&gt;&amp;lt;input type="hidden" .../&amp;gt;&lt;/code&gt; zu sehen. Der
eigentlich sichtbare Text wird aus dem aktuellen Model-Objekt gezogen und einfach drangehängt.
Wiedermal hat &lt;em&gt;formfield_for_dbfield&lt;/em&gt; keinen Zugriff auf das aktuell
zu ändernde Objekt, aber wie ihr seht, kann man es einfach in der &lt;em&gt;change_view&lt;/em&gt; Methode
auslesen und der Klasse global zuweisen.&lt;/p&gt;
&lt;p&gt;Stolperstein ist hier das Hidden-Input-Feld. Ein User mit &amp;ndash; ich nenne es mal:
genügend krimineller Energie &amp;ndash; kann mehr oder weniger einfach den Inhalt des 
Hidden-Feldes manipulieren. Vielleicht währe es sinnvoll, den Wert des Hidden-Feldes
zu verschlüsseln oder mit einer Prüfsumme (Hash) zu markieren. Andererseits, ein
User der es drauf anlegt, mit allen Mitteln so einen Titel zu ändern, sollte sowieso
keinen Zugang zur Admin-Ebene bekommen. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_wink.png" alt="Smiley:  ;-)" title=" ;-)" class="smiley"/&gt;&lt;/p&gt;
&lt;h3&gt;Andere Wege&lt;/h3&gt;
&lt;p&gt;Bei dieser Sache beschleicht mich mehr und mehr das Gefühl, dass der Ansatz einfach
zu kompliziert ist. Dieses &lt;em&gt;"formfield_for_dbfield hat keinen direkten Zugriff auf 
request, model-objekt, change_status, also holen wir das alles in den globalen Scope"&lt;/em&gt;
nervt.&lt;/p&gt;
&lt;p&gt;Aber es gibt auch andere Ansätze, am besten gefällt mir derzeit die Methode
auf &lt;a href="http://www.djangosnippets.org/snippets/937/"&gt;djangosnippets.org&lt;/a&gt;. Und wer ein wenig googelt findet noch hundert weitere Möglichkeiten, Admin-Felder &lt;em&gt;read-only&lt;/em&gt; zu setzen.&lt;/p&gt;
&lt;p&gt;Seis drum, letztendlich geht es mir bei dieser Artikelserie auch nicht darum,
den perfekten Lösungsvorschlag für Problem X zu zeigen, sondern viel mehr Mittel und
Wege zu zeigen, wie man ein solches Problem lösen &lt;em&gt;könnte&lt;/em&gt;. Am Ende kommen noch zehn
weitere Problemchen dazu (Stichwort: Permission-Handling) und jeder muss für sich
selbst wissen, was der beste Lösungsweg für sein Problem ist. Amen. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :-)" title=" :-)" class="smiley"/&gt;&lt;/p&gt;</description><pubDate>Mon, 22 Sep 2008 13:22:27 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/spass-mit-newforms-admin-read-only-felder.html</guid></item><item><title>Multiple Aktionen in Djangos Administration - Reloaded</title><link>http://www.freakydog.de/multiple-aktionen-djangos-administration-reloaded.html</link><description>&lt;p&gt;Vor mehr als einem halben Jahr hatte ich einen Blogpost zum Thema
&lt;a href="http://www.mahner.org/weblog/multiple-aktionen-djangos-administration/"&gt;Multiple Aktionen in Djangos Administration&lt;/a&gt; 
geschrieben. Nun ist schon einige Zeit ins Land gegangen, Django besitzt jetzt
den Status &lt;em&gt;Eins Punkt Null&lt;/em&gt; und das Admin wurde mit &lt;em&gt;newforms-admin&lt;/em&gt; generalüberholt.&lt;/p&gt;
&lt;p&gt;Das im Artikel angesprochene Prinzip funktioniert immernoch, ist aber nicht mehr
zeitgemäß. Daher freut es mich, dass es nun ein eigenes App für dieses Thema gibt:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://code.google.com/p/django-batchadmin/"&gt;django-batchadmin&lt;/a&gt; von
&lt;a href="http://blog.brianbeck.com/"&gt;Brian Beck&lt;/a&gt; liefert alles, um multiple Deletes in
Djangos Administrationsoberfläche auszuführen und erlaubt dazu noch, es mit eigenen
&lt;em&gt;Plugins&lt;/em&gt; zu erweitern. Das alles absolut konform mit newforms-admin. Sehr cool!&lt;/p&gt;
&lt;img src="http://media.brianbeck.com/images/batchadmin-1.png" alt=""/&gt;

&lt;p&gt;Getestet habe ich es leider noch nicht, der einzige Knackpunkt den ich auf Anhieb
gefunden habe ist, dass django-batchadmin zwingend &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;
benötigt. &lt;/p&gt;</description><pubDate>Mon, 15 Sep 2008 14:22:25 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/multiple-aktionen-djangos-administration-reloaded.html</guid></item><item><title>Zeig mal den Query</title><link>http://www.freakydog.de/zeig-mal-den-query.html</link><description>&lt;p&gt;Oft will man schnell mal wissen, was Djangos &lt;a href="http://docs.djangoproject.com/en/dev/ref/models/querysets/"&gt;DB-Api&lt;/a&gt; so im Hintergrund treibt, sprich: Welches SQL-Statement wird abgesetzt.&lt;/p&gt;
&lt;p&gt;Es gibt viele Apps die das übernehmen, der letzte Schrei derzeit ist Rob Hudsons &lt;a href="http://github.com/robhudson/django-debug-toolbar/tree/master"&gt;Django Debug Toolbar&lt;/a&gt;. Der Nachteil ist, dass die Ausgabe zumeist im Template stattfindet. Im Quellcode direkt gehts aber auch:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_sql&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="go"&gt;(&amp;#39;SELECT &amp;quot;weblog_entry&amp;quot;.&amp;quot;id&amp;quot; FROM &amp;quot;weblog_entry&amp;quot; ORDER BY &amp;quot;weblog_entry&amp;quot;.&amp;quot;published&amp;quot; DESC&amp;#39;, ())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Die Ausgabe erfolgt dann im Terminal bzw. im Logfile des Webservers. Neat! &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :-)" title=" :-)" class="smiley"/&gt;&lt;/p&gt;</description><pubDate>Thu, 11 Sep 2008 18:01:18 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/zeig-mal-den-query.html</guid></item><item><title>Merkzettel für Git</title><link>http://www.freakydog.de/merkzettel-fur-git.html</link><description>&lt;p&gt;Ein paar Stichpunkte zum Umgang mit &lt;a href="http://git.or.cz/"&gt;git&lt;/a&gt;; in erster Linie als Gedankenstütze für mich:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Der Master Branch ist der stabile Zweig. Wenn du ein Feature hinzufügen willst, erstelle einen neuen Branch und merge ihn anschließend mit dem Master Branch.&lt;/li&gt;
&lt;li&gt;Commite viel, commite oft. Commite jeden Pups.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git-rm&lt;/code&gt; löscht die Dateien wirklich endgültig von der Platte und entfernt sie nicht nur aus der Versionskontrolle. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_unhappy.png" alt="Smiley:  :-(" title=" :-(" class="smiley"/&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git-gui&lt;/code&gt; und &lt;code&gt;gitk&lt;/code&gt; sehen schrecklich aus, sind aber mächtig und produktiv.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.imendio.com/projects/giggle"&gt;Giggle&lt;/a&gt; sieht gut aus, kommt aber nicht an &lt;code&gt;gitk&lt;/code&gt; ran.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;projekt/.gitignore&lt;/code&gt; ist unnötig, 90% der Einträge gehören in die &lt;code&gt;~/.gitignore&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Subversion nervt immer mehr. &lt;code&gt;git-svn&lt;/code&gt; erst recht.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/"&gt;Github&lt;/a&gt; ruled!&lt;/li&gt;
&lt;li&gt;&lt;a href="http://meld.sourceforge.net/"&gt;meld&lt;/a&gt; ist das beste diff-Tool des Planeten. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Zu guter letzt noch meine .gitconfig:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;[gui]&lt;/span&gt;
    &lt;span class="na"&gt;recentrepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/home/martin/Workspace/foobar&lt;/span&gt;
    &lt;span class="na"&gt;fontdiff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;-family \&amp;quot;bitstream vera sans mono\&amp;quot; -size 8 -weight normal -slant roman -underline 0 -overstrike 0&lt;/span&gt;
&lt;span class="k"&gt;[user]&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;martin@mahner.org&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Martin Mahner&lt;/span&gt;
&lt;span class="k"&gt;[giggle]&lt;/span&gt;
    &lt;span class="na"&gt;compact-mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;main-window-maximized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;main-window-geometry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1600x1123+0+52&lt;/span&gt;
&lt;span class="k"&gt;[merge]&lt;/span&gt;
    &lt;span class="na"&gt;tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;meld&lt;/span&gt;
&lt;span class="k"&gt;[core]&lt;/span&gt;
    &lt;span class="na"&gt;excludesfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/home/martin/.gitignore&lt;/span&gt;
&lt;span class="k"&gt;[color]&lt;/span&gt;
    &lt;span class="na"&gt;branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;auto&lt;/span&gt;
    &lt;span class="na"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;auto&lt;/span&gt;
    &lt;span class="na"&gt;interactive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;auto&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;auto&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;und meine .gitignore:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;*.pyc
*.pyo
*~
*.swp
*.orig
.project
.pydevproject
.DS_Store
MANIFEST
dist
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</description><pubDate>Wed, 10 Sep 2008 17:51:34 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/merkzettel-fur-git.html</guid></item><item><title>Spass mit Newforms-Admin - Automatische Felder</title><link>http://www.freakydog.de/spass-mit-newforms-admin-automatische-felder.html</link><description>&lt;p&gt;Im letzten Artikel
&lt;a href="/weblog/spass-mit-newforms-admin-rowlevel-permissions/"&gt;Rowlevel-Permissions&lt;/a&gt;
ging es darum, wie man Einträge nur vom Benutzer selber bearbeiten lassen kann.
Wer den Artikel verfolgt und aufgebaut hat, wird festgestellt haben, dass die ganze
Sache einen gewaltigen Haken hatte:&lt;/p&gt;
&lt;p&gt;&lt;a rel="lightbox" href="http://static.mahner.org/assets/weblog/django/autorenfeld1.png"&gt;&lt;img
src="http://static.mahner.org/assets/weblog/django/autorenfeld1_klein.png"
alt=""/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In der Detailansicht konnte das Autorenfeld frei definiert werden, sprich
Benutzer A konnte sich für Benutzer B ausgeben und umgekehrt. In diesem Artikel
geht es darum, dem eingeloggten Autor automatisch den Artikel zuzuweisen.&lt;/p&gt;
&lt;h3&gt;Holzhammer&lt;/h3&gt;
&lt;p&gt;Die einfachste Lösung ist: Zeige das Autorenfeld gar nicht erst an und weise
beim Speichern des Artikels dem Autorenfeld den eingeloggten User zu. Das Ganze
ist schnell geschehen. Ähnlich wie die "newforms save()" Methode stellt auch 
ModelAdmin eine Methode zum Überschreiben zur Verfügung:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="c"&gt;# Autorenfeld ausblenden (mit #7937 geht das irgendwann mal einfacher)&lt;/span&gt;
    &lt;span class="c"&gt;# #7937: http://code.djangoproject.com/ticket/7973&lt;/span&gt;
    &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;&amp;#39;content&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Formular valdidieren, Autor setzen und speichern&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# Nur bei neuen Artikeln den User setzen&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Fertig ist die ganze Geschichte. Schnell, sicher und kaum zu überlisten.&lt;/p&gt;
&lt;h3&gt;Nicht ganz so krass&lt;/h3&gt;
&lt;p&gt;Vielleicht ist das aber auch schon einen Schritt zu weit. In meinem aktuellen Projekt möchte
ich es trotzdem dem Benutzer überlassen, den Autor zu setzen, allerdings mit einer
Kleinigkeit: Der aktuelle Benutzer soll in der Liste schon vorausgewählt sein.&lt;/p&gt;
&lt;p&gt;Das Ganze ist ein wenig mehr "tricky". In ModelAdmin gibt es wiederrum eine Methode,
die vor der Anzeige der Felder aufgerufen wird und mit der man ihr Verhalten 
und Aussehen (Widgets) beeinflussen kann: &lt;em&gt;formfield_for_dbfield&lt;/em&gt;.&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5
6
7
8
9&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;formfield_for_dbfield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;formfield_for_dbfield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# Autorenfeld hat als Vorauswahl den aktuellen User&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;author&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;initial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;In dieser Methode prüfen wir, ob das aktuelle Feld unser "Autorenfeld" ist, dann
wird ihm als Startwert (initial) der aktuelle User zugewiesen.&lt;/p&gt;
&lt;p&gt;Nächster Haken: Der obige Code funktioniert nicht, in der Methode &lt;em&gt;formfield_for_dbfield&lt;/em&gt;
hat man keinen Zugriff auf das request-Objekt. Ein wenig Patching muss her:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;change_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;formfield_for_dbfield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;formfield_for_dbfield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# Autorenfeld hat als Vorauswahl den aktuellen User&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;author&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;initial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Glücklicherweise ist ModelAdmin flexibel genug, das Problem zu lösen. Die Methoden
&lt;em&gt;add_view&lt;/em&gt; und &lt;em&gt;change_view&lt;/em&gt; werden vor der Anzeige des Formulares aufgerufen und
beiden steht das Request-Objekt zur Verfügung. Das ist genau der richtige Ort, das
Request-Objekt als globale Eigenschaft (self._request) der Klasse zuzuweisen.&lt;/p&gt;
&lt;p&gt;Somit kann man in der formfield-Methode einfach auf das "globale" Request-Objekt
zugreifen und mit den paar Handgriffen ist der Benutzer in der Autorenliste
vorausgewählt.&lt;/p&gt;
&lt;p&gt;&lt;a rel="lightbox" href="http://static.mahner.org/assets/weblog/django/autorenfeld2.png"&gt;&lt;img
src="http://static.mahner.org/assets/weblog/django/autorenfeld2_klein.png"
alt=""/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mit ein wenig Javascript kann man auch einfach aus diesen beiden Sachen eins machen:&lt;/p&gt;
&lt;p&gt;&lt;embed src="http://blip.tv/play/AcnZXgA" type="application/x-shockwave-flash" width="760" height="575" allowscriptaccess="always" allowfullscreen="true"&gt;&lt;/embed&gt;&lt;/p&gt;
&lt;p&gt;Und nicht vergessen: Auch hier ist es wieder möglich mit Permissions zu arbeiten.
So könnte ein Administrator immer den Autor setzen (zweite Variante), wo ein "normaler" User kein
Autorenfeld zu Gesicht bekommt und automatisch gesetzt wird (erste Variante).&lt;/p&gt;</description><pubDate>Mon, 25 Aug 2008 10:03:06 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/spass-mit-newforms-admin-automatische-felder.html</guid></item><item><title>Spass mit Newforms-Admin - Rowlevel-Permissions</title><link>http://www.freakydog.de/spass-mit-newforms-admin-rowlevel-permissions.html</link><description>&lt;p&gt;Newforms-Admin ist schon eine Weile verfügbar und daher wird es Zeit, sich
einmal mit den Features auseinander zu setzen. Dieser Artikel ist der Anfang
einer Reihe von Tipps und Tricks zu Newforms-Admin, die nacheinander alle
aufeinander aufbauen.&lt;/p&gt;
&lt;p&gt;Ein oft gewünschtes Feature im Admin-Interface ist "Lasse den Benutzer nur seine
eigenen Einträge bearbeiten", anders ausgedrückt: Rowlevel-Permissions. Eine der
einfachsten Aufgaben für das neue Admin-Interface.&lt;/p&gt;
&lt;p&gt;Fangen wir wieder einmal mit dem Model an, ein kleines Weblog ist ein gutes
Beispiel:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__unicode__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Um das Model mit dem Admin Interface administrieren zu können, ist eine
einfache ModelAdmin-Klasse notwendig, bestens aufgehoben in der Datei &lt;em&gt;admin.py&lt;/em&gt;
(&lt;a href="/weblog/newforms-adminoptions-besser-strukturieren/#comment15198"&gt;Jahaa&lt;/a&gt;):&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;myproject.weblog.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Entry&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;list_display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;&amp;#39;author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Soweit nichts spektakuläres, mit ein paar Einträgen von verschiedenen Usern sieht
die Übersichtsseite so aus:&lt;/p&gt;
&lt;p&gt;&lt;a rel="lightbox" href="http://static.mahner.org/assets/weblog/django/rowlevel1.png"&gt;&lt;img
src="http://static.mahner.org/assets/weblog/django/rowlevel1_klein.png"
alt=""/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Nun zur Aufgabe: Um dem Benutzer nur seine Beiträge bearbeiten zu lassen, lässt
Newforms-Admin auf einfache Weise zu, das Queryset für die Listenansicht zu
bearbeiten. Füge in deine ModelAdmin-Klasse eine Funktion "queryset" hinzu, die
die Einträge anhand der User/Autor-Beziehung filtert:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EntryAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c"&gt;# ...&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;queryset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_default_manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Das war es auch schon! Jeder Autor sieht ab sofort nur noch seine eigenen
Artikel:&lt;/p&gt;
&lt;p&gt;&lt;a rel="lightbox" href="http://static.mahner.org/assets/weblog/django/rowlevel2.png"&gt;&lt;img
src="http://static.mahner.org/assets/weblog/django/rowlevel2_klein.png"
alt=""/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Die Sache hat nur einen Haken: Auch Administratoren würden nur ihre eigenen
Artikel sehen, irgendwie nicht sehr sinnvoll. Um das Problem zu lösen, schaffen
wir uns am einfachsten eine &lt;a href="http://www.djangoproject.com/documentation/model-api/#permissions"&gt;zusätzliche Permission&lt;/a&gt;.
Erweitere also dein Model wie folgt:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5
6&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c"&gt;# ...&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;permissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;can_view_all&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Can view all Entries&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Nach einem "./manage.py syncdb" ist diese neue Permission auch schon verfügbar,
die du deinem User zuweisen kannst:&lt;/p&gt;
&lt;p&gt;&lt;a rel="lightbox" href="http://static.mahner.org/assets/weblog/django/rowlevel3.png"&gt;&lt;img
src="http://static.mahner.org/assets/weblog/django/rowlevel3_klein.png"
alt=""/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Leider ist nicht genug Voodoo in Django vorhanden, als dass alles automatisch
geht. In deiner Queryset-Redefinition musst du noch auf die neue Permission
prüfen, etwa so:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;queryset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c"&gt;# ...&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_perm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;weblog.can_view_all&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_default_manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_query_set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_default_manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Und fertig. "Normale" User können nun nur noch ihre eigenen Beiträge bearbeiten
während Administratoren oder User mit der Permission "can_view_all" alle
Einträge angezeigt bekommen.&lt;/p&gt;
&lt;p&gt;Zu guter letzt noch ein Hinweis: Theoretisch könnte man dieses Verhalten auch umdrehen
und eine Permission "Can view only his own entries" (*urgs*) schaffen. Das nur fürs Gedächtnis.&lt;/p&gt;</description><pubDate>Sat, 23 Aug 2008 19:58:45 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/spass-mit-newforms-admin-rowlevel-permissions.html</guid></item><item><title>Wiederverwendbare Django-Projekte</title><link>http://www.freakydog.de/wiederverwendbare-django-projekte.html</link><description>&lt;p&gt;Eine der Stärken von Django ist die Wiederverwendbarkeit von einzelnen Applikationen (aka &lt;em&gt;reusable apps&lt;/em&gt;). Will man aber ein komplettes Projekt veröffentlichen, sollte man sich auch Gedanken machen, wie man das komplette Projekt möglichst automatisch veröffentlichen kann. Anders ausgedrückt: der User soll möglichst wenig Pfade und Config-Einstellungen von Hand anpassen müssen.&lt;/p&gt;
&lt;p&gt;Die verschiedensten Einstellungen eines Django-Projekts befinden sich in der Regel in der &lt;em&gt;settings.py&lt;/em&gt; im Hauptverzeichnis deines Projekts, hier sind ein paar kleine Kniffe, wie du diese Einstellung möglichst dynamisch hälst:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ein Hinweis zuvor:&lt;/strong&gt; Die &lt;em&gt;settings.py&lt;/em&gt; ist eine Python-Datei und wird auch von Python geparst. Es ist keine Ansammlung statischer Definitionen sondern in ihr kann auch eine komplette Programmlogik ausgelagert werden.&lt;/p&gt;
&lt;h3&gt;1. Dynamische Pfade&lt;/h3&gt;
&lt;p&gt;Die erste Notwendigkeit vor dem Deployment einer Seite ist es, die Pfade zu den Templates und den statischen Mediendateien anzupassen. Diese Arbeit können wir dem User abnehmen, indem wir sie von vornherein dynamisch festlegen. Grundlage allen ist es, erst einmal zu wissen, welcher Pfad zum Projektverzeichnis führt:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="n"&gt;PROJECT_ROOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Die Variable PROJECT_ROOT enthält damit den Pfad zum aktuellen Verzeichnis, sei es /var/www/meinprojekt/ oder unter Windows C:\Dokumente und Einstellungen\username\meinprojekt\.&lt;/p&gt;
&lt;p&gt;Damit ist es möglich, die Pfade zu den Templates und den Mediendateien festzulegen. Eine Arbeit der der User vor dem Deployment also nicht mehr erledigen braucht:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4
5&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;MEDIA_ROOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROJECT_ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;site_media&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;TEMPLATE_DIRS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROJECT_ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;templates&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;h3&gt;2. Lass den SECRET_KEY geheim&lt;/h3&gt;
&lt;p&gt;Oft wird es vom User vergessen, den &lt;em&gt;SECRET_KEY&lt;/em&gt; neu zu setzen. Er wird bspw. als Salt für die Passwort-Generierung benutzt. Am sichersten und einfachsten ist es für den User, den &lt;em&gt;SECRET_KEY&lt;/em&gt; bei Bedarf sich selbst erstellen zu lassen: (Übernommen vom &lt;a href="http://hg.piranha.org.ua/byteflow/file/8b5437fb248a/settings.py#l67"&gt;Byteflow-Projekt&lt;/a&gt;)&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;SECRET_KEY&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;NameError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;SECRET_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROJECT_ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;secret.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;SECRET_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SECRET_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;IOError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;
            &lt;span class="n"&gt;SECRET_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&amp;amp;*(-_=+)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
            &lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SECRET_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;IOError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Please create a &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; file with random characters to generate your secret key!&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;SECRET_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Der kleine Schnipsel prüft, ob eine Variable &lt;em&gt;SECRET_KEY&lt;/em&gt; schon definiert wurde. Wenn nicht, erstellt er eine Datei &lt;em&gt;secret.txt&lt;/em&gt; im Projektverzeichnis und schreibt darin selbstständig einen zufälligen String. (Der Webserver muss in diesem Fall Schreibrechte für das Projektverzeichniss besitzen.)&lt;/p&gt;
&lt;h3&gt;3. Don't touch the settings&lt;/h3&gt;
&lt;p&gt;In der Bildbearbeitung gibt es einen Leitsatz: Don't touch the Pixels. Das bedeutet, wenn man ein Bild bearbeiten will, legt man sich zuvor eine Kopie der Bildebene an oder erstellt einen Snapshot, um im Bedarfsfall auf die Originaldaten zurück greifen zu können.&lt;/p&gt;
&lt;p&gt;Mit den Django-Settings verhält es sich ähnlich. Ich füge in alle meine Settings-Dateien am Ende diesen Schnipsel ein:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;pre&gt;1
2
3
4&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;local_settings&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Er versucht, eine Datei "local_settings.py" zu finden und übernimmt/überschreibt die Einstellungen aus dieser Datei in die globalen Einstellungen. Diese "local_settings.py" ist der perfekte Ort für die Einstellungen, die vom User in jedem Fall gesetzt werden müssen &amp;ndash; die Einstellungen für den Datenbankzugriff etwa.&lt;/p&gt;
&lt;p&gt;Der große Vorteil kommt aber erst zum Tragen, wenn man das Projekt regelmäßig aus einem VCS aktualisiert. Da im Idealfall keine Daten in der ursprünglichen "settings.py" geändert werden, gibt es keine Kollisionen mit Updates. Der User kann also das Projekt regelmässig aus dem VCS aktualisieren, ohne jedes Mal die Einstellungen neu schreiben zu müssen.&lt;/p&gt;
&lt;p&gt;Das waren drei kleine Kniffe, die das Deployment eines Projekts für den User sehr erleichtern. Das Ganze lässt sich noch beliebig fortführen, ist aber in den meisten Fällen projekt-abhängig. Hab ich noch etwas wichtiges vergessen, dann schreibt doch einen Hinweis in die Kommentare. &lt;img src="http://static.mahner.org/lib/gfx/icons/silk/emoticon_smile.png" alt="Smiley:  :-)" title=" :-)" class="smiley"/&gt;&lt;/p&gt;</description><pubDate>Sun, 17 Aug 2008 16:10:05 -0000</pubDate><guid isPermaLink="false">http://www.freakydog.de/wiederverwendbare-django-projekte.html</guid></item></channel></rss>
