Grundsätzliches zu einer Webapp

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
nemomuk
User
Beiträge: 862
Registriert: Dienstag 6. November 2007, 21:49

Sonntag 26. April 2009, 08:56

Hallo,

ich stehe vor folgendem, Problem:
- ich habe eine Aktion, zB kann man etwas kaufen, auf der Hauptseite ("/index/") ist ein Formular mit Menge etc.
- dieses Formular wird an "/buy/" ge-"postet"
- dieses Formular wird dann serverseitig auf Gültigkeit überprüft etc.
- nun soll bei Fehlermeldung etc. eine Statusmeldung erscheinen - diese sollten aber dann wieder unter "/index/" erscheinen
- dafür benötige ich ja normal ein redirect, nur wie kann ich hier die Statusmeldungen übergeben...?

Irgendwie ein relativ simples Problem, komme aber irgendwie nicht auf eine schön einfache Lösung. Wie würdet ihr das machen?

Danke![/list]
EnTeQuAk
User
Beiträge: 986
Registriert: Freitag 21. Juli 2006, 15:03
Wohnort: Berlin
Kontaktdaten:

Sonntag 26. April 2009, 09:19

Am einfachsten währe es wohl dir eine Art Wrapper namens `flash` zu schreiben, dieser fügt bei Aufruf an deine Session im Request ein neues Objekt `flash_messages` (type list) hinzu, oder erweitert diese. In einer Template Engine kannst du dann über Kontext-Modifikatoren diese Flash-Messages ins Template einbauen.

Einfaches Beispiel:

Code: Alles auswählen

def flash(message, success=None, classifier=None, session=None):
    """
    Flash a message (can contain XHTML).  If ``success`` is True, the flashbar
    will be green, if it's False, it will be red and if it's undefined it will
    be yellow.  If a classifier is given it can be used to unflash all
    messages with that classifier.
    """
    if session is None:
        session = getattr(current_request, 'session', None)
        if session is None:
            return False
    if not 'flmsg' in session:
        session['flmsg'] = [(message, success, classifier)]
    else:
        session['flmsg'].append((message, success, classifier))
        session.modified = True
    return True
Das Beispiel setzt vorraus das du mit werkzeug.contrib.sessions arbeitest, sollte aber leicht auf andere Systeme adaptierbar sein.

Grüße, Christopher.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Sonntag 26. April 2009, 09:37

Rails nennt dies eine "flash message" und nutzt dafür ein Cookie und eine Sessions, damit sich die Index-Seite nach dem Redirect daran erinnert, dass da noch etwas anzuzeigen war. Die Nachricht überlebt genau einen Redirect und wird dann automatisch entfernt.

Vielleicht hilft dir http://python-rum.org/wiki/WebFlash Auf JavaScript zu vertrauen finde ich jedoch nicht so gut und würde lieber eine Session auf dem Server benutzen.

Stefan
nemomuk
User
Beiträge: 862
Registriert: Dienstag 6. November 2007, 21:49

Dienstag 28. April 2009, 20:09

So, habe nun versucht das ganze mit Cookies zu lösen...

Wenn man nun die Statusnachrichten ansieht, summieren sich diese mit der Zeit immer weiter an... Ich finde das höchst merkwürdig, da muss es sich ja theoretisch immer um das gleiche Response Objekt handeln.

Ich kann den Fehler nicht entdecken... Wo liegt das Problem?

Vielen Dank!
Zuletzt geändert von nemomuk am Dienstag 28. April 2009, 21:44, insgesamt 1-mal geändert.
lunar

Dienstag 28. April 2009, 21:05

Was erwartest du denn, wenn du "msg" als Klassenattribut von "Response" definierst? Natürlich "summieren" sich die Nachrichten auf, weil jeder Zugriff auf "msg" das Klassenattribut trifft, so gesehen handelt es sich auch immer um "das gleiche Response-Objekt".

Nebenbei bemerkt gibt es "werkzeug.redirect", außerdem wirft deine Anwendung an diversen Stellen "NameError".

Das du an diversen Stellen das Rad neu erfindest, kommt noch hinzu ... Werkzeug ist ohne Zweifel eine tolle Bibliothek, aber wenn ich mir deinen Code anschaue, kann ich nicht umhin zu glauben, dass du mit einem vernünftigen Framework besser bedient wärst ...
nemomuk
User
Beiträge: 862
Registriert: Dienstag 6. November 2007, 21:49

Dienstag 28. April 2009, 21:30

upps... das war die alte Version,... aber trotzdem zugegeben: das ist ziemlich umständlich, was ich da fabriziert habe. Ich hatte mich da immer weiter in ein Konzept reingefrickelt, was nicht wirklich Sinn machte (was dann letztendlich darin geendet hat, dass ich mir sogar eine eigene redirect Funktion schreiben musste) - mache das Ganze nun über ein "Local"-Objekt.

Was hättest du sonst noch anders gemacht?
Zuletzt geändert von nemomuk am Mittwoch 29. April 2009, 05:21, insgesamt 1-mal geändert.
lunar

Mittwoch 29. April 2009, 00:29

Alles, was mir aufgefallen ist:
  • Wofür benötigst du den Dateinamen des View-Moduls als kontextlokales Attribut?
  • Warum unterscheidest du zwischen "prev_msg" und "msg"? Wenn du dem Nutzer etwas zu sagen hast, kannst du das auch gleich tun, anstatt Nachrichten immer über mindestens einen Request zu verschleppen.
  • Du speicherst Nachrichten im HTML-Format im Cookie? Mal abgesehen davon, dass "<br />" dir den knappen Cookie-Speicherplatz stiehlt, ist das unsinnig, weil du vielleicht mal in die Verlegenheit kommen könntest, die Daten anderweitig anzuzeigen. Da wäre ein anderes Trennzeichen sinnvoller, das Template sollte dann eine Liste mit allen Nachrichten erhalten und selbst entscheiden, wie sie angezeigt werden sollten. Vergiss nicht, für die Darstellung ist das Template verantwortlich und nicht der Controller oder die Applikation.
  • property funktioniert auch als Dekorator, die explizite Definition von "get_msg" ist daher überflüssig.
  • Was hat PROJECT_NAME im Endpoint zu suchen? Das lässt darauf schließen, dass du nicht so ganz verstanden hast, wie Deployment funktionieren sollte.
  • Wieso erzeugst du die URL_MAP nicht gleich in urls.py? Es ist ziemlich unsinnig, eine konstante Datenstruktur in eine andere konstante Datenstruktur zu überführen, du kannst auch einfach gleich alles bei der Initialisierung von URL_MAP angeben.
  • Was haben die Einstellungen (settings.py) im Anwendungspaket zu suchen?
  • Wieso erzeugst du in "render_template()" für jedes Template-Rendering ein separates Environment?! Du solltest ein Template-Environment global erzeugen, und "msg" in "**context" verschieben!
  • render_msg() ist überflüssig, siehe oben
  • Authentifizierung (und Authorisierung) ala "login_required()" solltest du nicht selbst bauen, dieses Rad wurde schon oft erfunden. Nimm Authkit, Barrel, repoze.what oder whatever ...
  • In "login" fehlt jegliche Input Validierung. Benutzereingaben, selbst wenn sie nur aus Benutzername und Passwort bestehen, ist nie zu trauen, und sie müssen immer validiert werden.
Das ist alles, was mir auf die Schnelle aufgefallen ist. Ich kann mich nur wiederholen: Ich glaube, du wärst mit einem vollwertigen Framework besser bedient. Die Existenz von "urls.py" und "settings.py" sowie der zwanghafte Versuch, eine Art "Projekt" mit "Unteranwendungen" zu schaffen, deuten stark darauf hin, dass du versuchst, auf irgendeine Weise Django nachzuprogrammieren. Wenn dem so ist, dann kannst du auch gleich Django nutzen. Wenn dir Django nicht gefällt, so solltest du erstmal Alternative probieren, bevor du Werkzeug nutzt. Werkzeug ist kein Framework, sondern ein Toolkit, und ist daher nicht für schnelle Ergebnisse und einen leichten Einstieg geeignet, sondern benötigt schon ein bisschen Wissen.
nemomuk
User
Beiträge: 862
Registriert: Dienstag 6. November 2007, 21:49

Mittwoch 29. April 2009, 05:21

Ich bedanke mich für deine ausführliche Schilderung! Vllt. hast du recht und ich versuche mir da selbst etwas zu basteln, was eigentlich nicht wirklich notwendig ist. Auf der anderen Seite ist mir Django zu aufgeblasen, mir wäre etwas "schlankeres" lieber...
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mittwoch 29. April 2009, 07:34

Inwiefern aufgeblasen? Inwiefern stört das?

(Django ist ja immerhin kein J2EE)

Ansonsten kannst du dir ja Glashammer ansehen, wenn du bei Werkzeug bleiben willst.

Und wo ist dein Code von dem lunar spricht?
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Mittwoch 29. April 2009, 09:06

SchneiderWeisse hat geschrieben:Auf der anderen Seite ist mir Django zu aufgeblasen, mir wäre etwas "schlankeres" lieber...
Hallo SchneiderWeisse!

Dann versuche es doch mal einen Tag lang mit CherryPy. :D Das ist nicht aufgeblasen und trotzdem nimmt es dir viel Arbeit ab.

Zur Datenweitergabe kannst du alle Techniken einsetzen die du möchtest. Aber Sessions sind sicherlich die einfachste Möglichkeit dafür -- auch mit CherryPy.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Mittwoch 29. April 2009, 09:29

Ich finde Django übrigens gar nicht aufgeblasen. Alles, was man braucht, um loszulegen:

1. `django_admin.py startproject prjct; cd prjct; chmod +x manage.py; ./manage.py startapp blog` ausführen.

2. Folgendes an `settings.py` anfügen (bzw. die passenden Eigenschaften ändern):

Code: Alles auswählen

DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = 'prjct.db'
TEMPLATE_DIRS = ('templates',)
INSTALLED_APPS += ('django.contrib.admin', 'prjct.blog',)
MEDIA_ROOT = 'static/'
MEDIA_URL = '/static'
3. `mkdir templates static` zum Anlegen der Verzeichnisse ausführen.

4. In `urls.py` das Admin-UI aktivieren und `/static` binden:

Code: Alles auswählen

from django.conf import settings
from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns('',
    (r'^admin/', include(admin.site.urls)),
    (r'^static/(.*)', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT})
)
5. Modell in `blog/models.py` definieren:

Code: Alles auswählen

class Post(models.Model):
    title = models.CharField(max_length=200)
    text = models.TextField()
    pub_date = models.DateTimeField(default=datetime.now)
    
    class Meta:
        ordering = ['-pub_date']
6. Admin-Formular in `blog/admin.py` (was blöderweise nicht mit angelegt wird) definieren:

Code: Alles auswählen

from django.contrib import admin
from models import Post

admin.site.register(Post)
7. Mit `manage.py sycndb` Datenbank anlegen und mit `manage.py runserver` das System starten.

8. Zu <http://localhost:8000/admin> gehen, und einen Blog-Eintrag anlegen.

9. Startseite für den Blog in `urls.py` als `(r^$, 'blog.views.index')` vereinbaren. Dann die View-Funktion schreiben:

Code: Alles auswählen

from django.shortcuts import render_to_response
from models import Post

def index(request):
    return render_to_response("blog/index.html", {'posts': Post.objects.all()[:10]})
10. Template erstellen, auf <http://localhost:8000/> und fertig.

Sollte ich Django anpassen dürfen, würde ich dafür sorgen, dass `startproject` und `startapp` diese Vorgaben gleich machen, sodass man sie nicht extra alle aufschreiben muss, sondern später dann nur auskommentieren muss. Ein bisschen mehr scaffolding a la Rails wäre nicht verkehrt. In jedem Fall sollte IMHO das Admin-UI, standardmäßig aktiviert sein.

Stefan
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Mittwoch 29. April 2009, 12:26

gerold hat geschrieben:Zur Datenweitergabe kannst du alle Techniken einsetzen die du möchtest. Aber Sessions sind sicherlich die einfachste Möglichkeit dafür -- auch mit CherryPy.
Hallo!

Hier findest du ein kleines Beispiel, wie man so etwas mit CherryPy machen kann:
http://paste.pocoo.org/show/114935/

EDIT: Das Beispiel sieht nur so groß aus, weil ich die Vorlagentexte ebenfalls mit in das Beispiel aufgenommen habe. Normalerweise legt man diese Vorlagentexte in eingenen Dateien ab.

mfg
Gerold
:-)
Zuletzt geändert von gerold am Mittwoch 29. April 2009, 12:44, insgesamt 1-mal geändert.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
lunar

Mittwoch 29. April 2009, 12:34

Wenn jetzt schon wieder jeder sein Lieblingsframework in den Raum wirft, kann ich auch gleich Pylons erwähnen. Das hat bereits was fertiges für Flash Messages ;)

Django aber wäre für ihn wohl das beste, immerhin versucht er es ja nachzuprogrammieren.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Mittwoch 29. April 2009, 12:42

lunar hat geschrieben:Django aber wäre für ihn wohl das beste, immerhin versucht er es ja nachzuprogrammieren.
Hallo lunar!

Warum denkst du, dass jemand mit diesen simplen Anforderungen
SchneiderWeisse hat geschrieben:- ich habe eine Aktion, zB kann man etwas kaufen, auf der Hauptseite ("/index/") ist ein Formular mit Menge etc.
- dieses Formular wird an "/buy/" ge-"postet"
- dieses Formular wird dann serverseitig auf Gültigkeit überprüft etc.
- nun soll bei Fehlermeldung etc. eine Statusmeldung erscheinen - diese sollten aber dann wieder unter "/index/" erscheinen
- dafür benötige ich ja normal ein redirect, nur wie kann ich hier die Statusmeldungen übergeben...?
Django nachprogrammieren möchte?

Das ist doch etwas, was mit jedem, auch noch so kleinen, Framework gemacht werden kann. Man braucht nur eine passende Strategie und etwas Erfahrung dafür. Wie einfach das mit CherryPy ist, habe ich im obigen Beispiel aufgezeigt. Und ich kann mir nicht vorstellen, dass es mit Werkzeug viel komplizierter ist.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mittwoch 29. April 2009, 12:49

gerold hat geschrieben:Warum denkst du, dass jemand mit diesen simplen Anforderungen
SchneiderWeisse hat geschrieben:- ich habe eine Aktion, zB kann man etwas kaufen, auf der Hauptseite ("/index/") ist ein Formular mit Menge etc.
- dieses Formular wird an "/buy/" ge-"postet"
- dieses Formular wird dann serverseitig auf Gültigkeit überprüft etc.
- nun soll bei Fehlermeldung etc. eine Statusmeldung erscheinen - diese sollten aber dann wieder unter "/index/" erscheinen
- dafür benötige ich ja normal ein redirect, nur wie kann ich hier die Statusmeldungen übergeben...?
Django nachprogrammieren möchte?
Offensichtlich weil seine Implementation einen sehr Django-ähnlichen Aufbau hatte. Meine letztze Werkzeug-Applikation hatte auch einen ähnlichen aufbau zu Django, weil das System durchaus brauchbar ist.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Antworten