Zeile verdoppeln in Eclipse
Im Editor Scite gibt es ein wunderbar kleines Feature: Mit der Tastenkombi CTRL+D verdoppelt man die aktuelle Zeile oder Auswahl. Diese Funktion braucht man vielleicht nicht so oft in der Programmierung, ich vermisse sie aber insbesondere im Umgang mit Javascript und HTML. Leider ist in den meisten anderen IDEs diese Tastenkombination mit "Zeile löschen" voreingestellt was einfach nur nervt.
Wolfram Kriesing hat nun eine Möglichkeit gezeigt, wie man Komodo dieses Verhalten beibringt und gleichzeitig habe ich noch einmal geschaut, ob Eclipse/Aptana das nicht auch irgendwie unterstützt.
Und siehe da, es geht sogar von Haus aus. CTRL+ALT+UP ist die Standardkombi dafür, die sich natürlich einfach neu mappen lässt. ![]()
Newforms AdminOptions besser strukturieren
Wer in den letzten Tagen seine schon etwas ältere Django-trunk Version aktualisiert hat, dürfte sein blaues Wunder erlebt haben, positiv oder negativ. Unter anderem hat Newforms Admin ja endlich Einzug gehalten.
Anders als bisher werden Einstellungen der Models nun nicht mehr in einer Subklasse Admin des Models definiert, sondern in einer eigenen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Entry(models.Model): title = models.CharField(max_length=255) slug = models.SlugField(blank=True, db_index=True) content = models.TextField() published = models.DateTimeField(auto_now_add=True) class EntryAdmin(admin.ModelAdmin): save_on_top = True list_select_related = True date_hierarchy = 'published' list_display_links = ('title') search_fields = ('title', 'content') admin.site.register(Entry, EntryAdmin) |
Nun gibt es prinzipiell zwei Möglichkeiten, diese Admin-Optionen an die Models zu "binden":
-
autodiscover() durchsucht die Apps nach einer Datei "admin.py" und importiert dieses Modul automatisch.
-
Per Hand und überall mittels "admin.site.register('model', 'options')" wie im obigen Beispiel.
Ich bevorzuge den zweiten Weg, da ist weniger Voodoo im Spiel und ich kann penibel festlegen, welches App im Admin erscheint und welche Optionen ihm zugewiesen werden.
Bei meinem aktuellen Projekt habe ich dafür eine Basisklasse "DefaultModelAdmin" für alle ModelAdmins erstellt und leite davon alle anderen ModelAdmin-Klassen ab:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # File: apps/adminprefs/defaults from django.contrib import admin class DefaultModelAdmin(admin.ModelAdmin): save_on_top = True list_select_related = True # File: models.py/admin.py from django.contrib import admin from adminprefs.defaults import DefaultModelAdmin class EntryAdmin(DefaultModelAdmin): date_hierarchy = 'published' list_display_links = ('title') search_fields = ('title', 'content') admin.site.register(Entry, EntryAdmin) |
Das ist nicht nur DRY sondern wird in Zukunft auch helfen, wenn einmal ein zentraler Eingriff für alle Admin-Sachen nötig ist.
ModelsForms mit Choices validiert nicht korrekt
Um es vorweg zu nehmen, dies ist die Beschreibung eines Bugs in Django. Ja, dafür gibt es schon ein Ticket und nein, es ist derzeit noch nicht im Trunk gefixt.
Oki, wer bis hierhin durchgehalten hat, weiter gehts: ![]()
Angenommen wir haben dieses simple Model, die Werte des Testfeldes bestehen aus den vorher definierten Choices.
1 2 3 4 5 6 7 8 9 10 11 12 | from django.db import models CHOICES = ( (u'Bube', u'Bube'), (u'Dame', u'Dame'), (u'Koenig', u'Koenig'), (u'Ass', u'Ass'), ) # Das Model class TestModel(models.Model): testfeld = models.CharField(max_length=30, choices=CHOICES) |
Um ein simples Formular daraus zu erstellen ist ModelForms ideal:
1 2 3 4 5 6 | from testprojekt.models import TestModel from django import forms # Django 1.0 Alpha wohooo! class TestForm(forms.ModelForm): class Meta: model = TestModel |
Einfacher gehts kaum, mit den paar Zeilen hat man schon ein komplettes Formular parat. Aber dass wußtest du sicher schon, also weiter. Basteln wir uns eine einfache Ausgabe:
1 2 3 4 5 6 7 8 9 10 | >>> f = TestForm() >>> f.as_ul() # Beispielausgabe <select name="testfeld" id="id_testfeld"> <option value="Bube">Bube</option> <option value="Dame">Dame</option> <option value="Koenig">Koenig</option> <option value="Ass">Ass</option> </select> |
Alles korrekt bis hier hin. Angenommen das Formular wird nun auf der Webseite angezeigt, der User wählt "Bube" und schickt das Formular ab:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> f = TestForm(data={'testfeld': 'Bube'}) >>> f.as_ul() <select name="testfeld" id="id_testfeld"> <option value="Bube" selected="selected">Bube</option> <option value="Dame">Dame</option> <option value="Koenig">Koenig</option> <option value="Ass">Ass</option> </select> >>> f.is_valid() True |
Das Formular ist valide, weil "Bube" ja eine korrekte Auswahl für das Feld 'testfeld' ist. Aber zur Erinnerung: SELECT-Felder in HTML sind auch nur stinknormale Datenfelder und der Browser prüft nicht, ob der abgesendete Wert auch des Feldes auch vorher in den SELECT-Options definiert wurde. Ändern wir also mal die Werte der Formulardaten:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> f = TestForm(data={'testfeld': 'Boeses Maedchen'}) >>> f.as_ul() <select name="testfeld" id="id_testfeld"> <option value="Bube">Bube</option> <option value="Dame">Dame</option> <option value="Koenig">Koenig</option> <option value="Ass">Ass</option> </select> >>> f.is_valid() True |
Wem das nicht schlimm erscheint: Diese Daten speichert das Formular leider auch in die Datenbank, das Model selbst validiert also nicht noch einmal nach.
1 2 3 | >>> objekt = f.save() >>> objekt.testfeld u'Boeses Maedchen' |
Ganz fatal in meinen Augen. Zur Erinnerung, eigentlich sollte das testfeld im Model nur die Daten Bube, Dame, Koenig und Ass annehmen.
Wie kann man eigentlich "fremde" Daten in ein Select-Feld schreiben? Nichts einfacher als das, die bekannte Firefox-Extension "Web Developer Toolbar" hat eine eigene Funktion dafür:
Mein derzeitiger Lösungsvorschlag sieht so aus, dass ich einfach mit einer clean-Methode auf das Vorhandensein der Eingabe in den Choices prüfe:
1 2 3 4 5 6 7 8 9 10 11 12 | from testprojekt.models import TestModel, CHOICES from django import forms class TestForm(forms.ModelForm): def clean_testfeld(self): if not self.cleaned_data['testfeld'] in dict(CHOICES): raise forms.ValidationError(u'%s is not a valid choice' % (self.cleaned_data['testfeld'])) return self.cleaned_data['testfeld'] class Meta: model = TestModel |
Dann klappts auch wie gewünscht.
1 2 3 4 5 6 7 8 9 10 11 | >>> f = TestForm(data={'testfeld': 'Boeses Maedchen'}) >>> print f.is_valid() False >>> f.as_ul() errorlist: "Boeses Maedchen is not a valid choice" <select name="testfeld" id="id_testfeld"> <option value="Bube">Bube</option> <option value="Dame">Dame</option> <option value="Koenig">Koenig</option> <option value="Ass">Ass</option> </select |
Durch das derzeitge Release von Django 1.0 Alpha gehe ich aber mal davon aus, dass dieser Bug in nächster Zeit gefixt wird. Ein Patch exisitiert ja schon.
Resignation
Ich will das aber so.
Ist der Satz von einem Kunden, ab dem man einfach resignieren muss und auch die sinnfreiesten Änderungen durchführen sollte. Mehr schreibe ich dazu nicht – zu deprimierend – nur so viel: Ein dritter "Usability-Profi" hatte seine Finger im Spiel.
Das machen aber alle so.
Gib mir Pagerank
Es ist weder Montag noch Freitag. Warum werde ich dann heute mit solchen Kundenanfragen gestraft?
warum haben wir auf unserer hp kein pakerank ??
wenn moeglich bitte schnellstens einrichten !
Das ist Pagerank.