20100716

Passfotos werden endlich wieder angezeigt. In Einem sind nun auch die Comboboxen wieder kontextsensitiv. Der Konfigurationsparameter before_row_edit funktioniert jetzt (sieht aus als ob Funktionen in Javascript anders gehandhabt werden als normale Werte, jedenfalls musste ich von der Funktion ein createDelegate() machen). Lino.load_picture ist jetzt eine Methode von Lino.WindowWrapper(). Bei data_record wird Lino.WindowWrapper.load_master_record() ja gerufen, bevor der Component gerendert ist. Das verträgt er jetzt auc, er macht dann ein on(‘render’).

Außerdem sind die Passfotos jetzt erstmals anklickbar (das war subtil… die BoxComponent kriegt nicht nur das onlick-Event definiert, sondern auch cursor:pointer).

Detail-Fenster hatte keine Buttons (ls_bbar_actions ist ja nicht für den WindowWrapper, sondern für GridPanel und FormPanel).

Lino.grid_afteredit funktioniert wieder. Ist jetzt auch eine Methode on_afteredit von Lino.GridPanel(). Für ComboBoxen ist noch die unschöne Zeile p[e.field+'Hidden'] = e.value; nötig.

Lino.id_renderer() testet nun record.phantom statt record.id == -99999 (wie schon im extjsu gefunden) und (neu) wird nur für AutoField als renderer verwendet. Denn sonst sah man im Phantom-Record von Country den eingegebenen isocode erst wenn der Record abgespeichert worden war.

Wenn DEBUG False ist, dann included er jetzt die ext-all.js statt der ext-all-debug.js. Allerdings fängt Django bei DEBUG False die Http505-Exceptions ab und will sie formatieren, was ihm nicht gelingt weil ich dafür noch gar keine Templates installiert habe. Deshalb vier neue Dateien in dsbe/templates, die ich einfach aus django.admin kopiert und ein wenig angepasst habe. Zum Beispiel habe ich mehrere {% load adminmedia %} entfernt, weil die natürlich nicht funktionieren wenn django.contrib.admin nicht in INSTALLED_APPS ist.

Die Performance ist deutlich besser als mit extjsu, aber man kann noch nicht damit prahlen. Wo sind die größten Bottlenecks? Auf dem Server oder auf dem Client? Ich habe begonnen, FireBugs console.profile() bzw. console.time() zu nutzen. Bin noch nicht sicher, wo ich das einbauen muss, damit es sinnvoll ist. Hier ist eine Doku der console-API: http://getfirebug.com/wiki/index.php/Console_API

In der Grid kann man jetzt auch wieder ein neues Land erstellen, und man kann Notizarten konfigurieren (die Sache mit den neuen Choosern). Die Lösungen dazu waren in der ext_store.py, die ich integral aus extjsu übernehmen konnte.

Runterladen als CSV (http://127.0.0.1:8000/api/contacts/Persons?fmt=csv) funktionierte nicht. Behoben.

13.05 Uhr : Check-in Lino und DSBE mit oben beschriebenen Änderungen.

14.26 Uhr : Close-Buttons funktionieren. Wenn ein neues Fenster geöffnet wird, wird das vorherige Fenster nicht gelöscht, sondern bleibt in einer Historik (Lino.hidden_windows) erhalten. Beim Schließen eines Fensters zeigt er das vorherige Fenster an. er macht das, indem er die main_area holt (die immer ein Ext.Container ist) und dann mit Ext.Container.replace() das window ersetzt. Neue Klasse Lino.IndexWrapper() war nötig, damit auch die leere Hauptseite restauriert werden kann. Diese hat ja keine Tool-Buttons und kann deshalb nicht geschlossen werden. Die Fensterhistorik wird übrigens nicht gelöscht, wenn man einen Befehl des HM startet. Habe nicht probiert, wie das wäre, aber das momentane Verhalten scheint mir okay. Zwei Details in der /todo für später mal.

16 Uhr : Die Grid-Elemente im Detail-Fenster funktionieren wieder. Lino.load_slavegrid ersetzt durch Lino.GridPanel.load_slavegrid(). (Die Namensgebung und Verteilung dieser Methoden könnte bei Gelegenheit verbessert werden.)

Jetzt möchte ich mal endlich probieren, ob ich prev/next-Buttons bzw. pgup/pgdn-Tasten im Detail-Fenster hinbekomme. django hat ja die Methoden Model.get_next_by_FOO und Model.get_previous_by_FOO, aber ich fürchte, dass die hier nichts nützen.

Ich muss zunächst im URI-API einbauen, dass er auch bei einzelnen Elementen einen ReportActionRequest macht (also insbesondere order_by und quick_search auswertet). Ich habe dann also einen Report und ein Element. Wie findet man die Position (den index) eines Elements in einem QuerySet? Ha, hier ist die Frage auf Stackoverflow beantwortet: http://stackoverflow.com/questions/1042596/get-the-index-of-an-element-in-a-queryset

In lino.ui.extjsw.ext_ui.elem2rec() implementiere ich das wie folgt:

prev = None
next = None
g = enumerate(ar.queryset) # a generator
try:
    while True:
        index, item = g.next()
        if item == elem:
            if index > 0:
                prev = ar.queryset[index-1]
            i,next = g.next()
            break
except StopIteration:
    pass

Alternativ würde auch folgendes funktionieren:

prev = None
next = None
g = enumerate(ar.queryset) # a generator
for index, item in g:
    if item == elem:
        if index > 0:
            prev = ar.queryset[index-1]
        try:
            i,next = g.next()
        except StopIteration:
            pass
        break

Ich glaube (ohne sicher zu sein), dass die erste Methode effizienter ist, weil die try ... except nur einmal aufgebaut wird und auch ein for ... in generell teurer als ein while True ist.

Dieser Teil funktioniert. Jetzt muss ich natürlich noch die Buttons einbauen. Und dann muss ich noch dafür sorgen, dass die gewünschten query-Parameter korrekt an die Detail-Abfrage weitergeleitet werden. Er darf z.B. nicht einfach die offset und limit von der Grid übernehmen, weil man sonst nicht über die erste Seite der Grid hinaus blättern könnte. Ich mach mir freilich auch noch Sorgen, wie lange elem2rec() dauert, wenn ich den siebzehntausendsten Record einer Liste von 20000 anfrage…

Für die Buttons wäre ja ein echter Ext.PagingToolbar am schönsten. Leider verlangt der einen Store. Vielleicht gar nicht “leider”? Meine tolle prev/next-Suche in elem2rec() wäre dann zwar unnütz, aber wenn ich einen Store mit pageLen=1 machte? Dann würde ein Detail-Fenster gar nicht mehr api_elem_view sondern api_list_view benutzen. Und ich brauche mir keine Sorgen wegen möglicher Performanceproblem zu machen. Aber dann kann ich statt des pk ja gleich den index im URI-API einführen. Und ist das nicht ein bisschen Overkill, einen eigenen Store anzulegen nur zum Navigieren?

Neue kurzfristige To-Do-Liste:

  • Übersetzungen
  • Warum gibt es zweimal Luc Saffre?
  • Geburtsort einer Person FK zu City?