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. Smiley:  :-)

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: Smiley:  :-)

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.