pyMFrame :: Ein minimalistisches Webframework in Python

Hintergrund

Der Grund zur Entwicklung von pyMFrmae waren Überlegungen zum Thema Architektur eines minimalen Webframeworks.

Die Ideen hinter diesen Überlegungen wurden mittels der Sprache Python umgesetzt.

Hinweis

Diese Software dient lediglicht zu Test und Schulungszwecken. Von einer Benutzung im Produktionsbetrieb wird abgeraten. Der Autor übernimmt keinerlei Verantwortung für das korrekte Funktionieren des Programms.

Ressourcen

Download Sourcecode (leere Anwendung)

Dokumentation auf Google Drive

Demo (Zugriff admin/admin)

Kontakt zum Entwickler

Ziel

Entwickung eines Webframeworks mittels der Sprache Python als Übungs Projekt. Einsatz bei einfachen Projekten auf 'Standardservern' Um eine möglichst einfache Entwicklung zu ermöglichen, sind keine über den Python Standard hinausgehenden Klassen (mit Ausnahme ggf. notwendiger Datenbanktreibern) verwendet worden. Es wurde weitesgehend auf die Verwendung von Metasprachen verzichtet. Uebersicht.jpg

Umsetzung

Entwickeln von Klassen und Hilfsroutinen um mit möglichst geringen Aufwand eine Datenbankgestütze Anwendung zu Entwickeln (rapid prototyping). Es wird das MVC Modell unterstützt. Der Zugriff auf Datenbanktabellen wird gekapselt, um einen möglichst hohe Unabhängikeit der Datenbak zu ermöglichen. Die Verwaltung von Datenbankinhalten soll weitestgehende automatisiert erfolgen. Keine expliziten schreibe und Leseroutinen.

Basis

Python 2.7, Datenbanktreiber (sqlite, mySql, Oracle)

Templates

Es existieren zwei Arten von Templates.

Container

Container bilden den Rahmen fuer das rendering einer Ausgabe. Diese sind im Grunde eine HTML Seite und enhalten keine dynamischen Elemente sondern nur Platzhalter.

Auswahl von Platzhalten

Platzhaler

Inhalt

$body

Enhält das Ergebnis eines Controllers

$menu

Menü

$flash

Benutzungshinweise, Meldungen

Viewer

Viewer enhalte eingebetetten Pythoncode.

Der Code basiert auf - Tomer Filiba http://code.activestate.com/recipes/496702/ und wurde vom Autor erweitert.

Beispiel

<h1 class="blockname">Registrier und Verwaltungsprogramm der WiW&ouml; Landesaktion 2013</h1>
<%
if 'gruppe' in rights:
   out('<p id="infobox">Zur Gruppenregistrierung auf Gruppenregistrierung klicken</p>')
else:
   out('<p id="infobox">Verwaltung der Anmeldedaten</p>')
%>

Controller

Controller sind Python Klassen. Diese werden im Navigationsystem registriert und über eine CGI Variable (path) angesprochen.

Siehe dazu Module->Navigationssystem.

Vor dem Aufruf werden die Rechte überprüft und die Ausführung ggf. verhindert.

Beispiel eines einfachen Controllers

# -*- coding: iso-8859-15 -*-
from controller import Controller

class RootController(Controller):

   def get(self):
      # Ermittle Rechte      
      rights = self.main.authen.getRights()        

      self.view('view.tpl',
                param={
                   'rights':rights
                   })

Im o.A. Beispiel werden dem Viewer Paramter mitgegeben.

Das Framework stellt ein Navigatissystem (Menüs) zur Verfügung. Im Deklarationsteil werden der Anzeigename, der Controllername (optionanl) die Sichtbarkeit (optional) und die Rechte notiert.

Die Parameter eines Menüeintrags können programmatisch im Controller beinflusst werden.

      if self.main.getAttribute('grpKennung') == None:
         for path in [
            '/root/gruppenregistrierung/gruppenstammdaten',
            '/root/gruppenregistrierung/teilnehmer',
            '/root/gruppenregistrierung/report'
            ]: self.main.setEntryDisplay(path,False)

Beispiel eines Menüeintrags

       {
       'path':'/root/gruppe',
       'controller':'GruppeController',
       'text':'Gruppe',
       'display':True,
       'rights':['admin'],
     } 

Datenkbankzugriff

Der Datenbankzugriff ist gekapselt im Modul dbaccess.

Ziel ist es, dass der Nutzcode keine nativen SQL Anweisungen enhält.

Durch die Angabe des Datentyps wird beim Befüllen eines Feldes der Type überprüft.

Domain.jpg

Beispiel einer Domain

# -*- coding: iso-8859-15 -*-
from dbaccess.core   import *

class BuecherDomain(Domain):
   bueID                = None
   bueTitel             = None
   bueISBN              = None
   autID                = None
   bueErscheinungsdatum = None

   meta = {
      'tablename':'buecher',
      'primarykey':'bueID',
      'fields':{
         'bueID'                 : {'dbfield':'bueID',               'type':'Integer'},
         'bueTitel'              : {'dbfield':'bueTitel',            'type':'String'},
         'bueISBN'               : {'dbfield':'bueISBN',             'type':'String'},
         'autID'                 : {'dbfield':'autID',               'type':'Integer'},
         'bueErscheinungsdatum'  : {'dbfield':'bueErscheinungsdatum','type':'AnsiDate'}
      }
   }

   def getDatasource(self,selected=None):
      """
      Erzeugt eine Datasource fuer die Verwendung in
      taglib.promptinput
      """
      retval = []
      for domain in self.eachDomain(where=None,orderby=None):
         retval.append([domain.bueID,domain.bueID])
      return retval

   # ### HANDLER
   #
   #     HINT:
   #        Handler liefern [True|False] Zurueck.
   #        Bei False wird die Datenbankaktion abgebrochen
   #
   #     Diese Methoden koennen, wenn sie nicht benoetigt wereden
   #     aus dieser Datei entfernt werden.
   #
   #     Fehlermeldungen koennen mit self.addError("Meldung")
   #     angegben werden.
   #

   def checkISBN(self,value):
      """
      Pruefe die Laenge von ISBN

      @param value         ISBN

      @return  True|False  
      """
      
      if len(value) != 17:
         self.addError('Laengenfehler bei ISBN Nummer')
         return False

      return True
      
   def onCgiField(self,fieldname,value):
      """
      Wenn die Domain ueber das CGI befuellt wir
      wird bei jedem Feld dieser Handler aufgerufen.


      @param fieldname         Feldname
      @param value             Inhalt aus dem CGI

      @param [True|False]      wird False uebergeben so bricht das Laden ab
      """
      if fieldname=='bueISBN':
         return self.checkISBN(value)
                     
      return True


   def onWrite(self,mode=None):
      """
      Wird vor jeder schreibenden
      Operation aufgerfuen.

      @param  mode     Enthaelt insert/update/delete

      @return [True|False]

      """
      if mode in (self.INSERT,self.UPDATE):         
         return self.checkISBN(self.bueISBN)
      return True

   #
   # ################# END OF HANDLER

Die Klasse viewhandler

Um alle lesenden und Schreibenden Vorgänge in der Datenbank zu vereinfachen wurde diese Klasse entwickelt. Sie wird im Controller deklariert und liefert in Zusammenarbeit mit den Viewern die Bearbeitungsmasken.

Viewhandler.jpg

Beispiel der Verwendung

# -*- coding: iso-8859-15 -*-
"""
Dieser Controller wird vom Framework aufgerufen.

Er ist fuer ein GRID Layout optimiert.

"""
from controller         import Controller
from viewhandler        import Viewhandler
from helper.utility     import Utility
from conf.config        import Config

from domain.persondomain      import PersonDomain
from domain.gruppedomain      import GruppeDomain

class TeilnehmerController(Controller):

   viewhandler = None

   def get(self):
      gruppe      = GruppeDomain(db=self.db)
      grpKennung = self.main.getAttribute('grpKennung')
      gruppe.getOverKennung(grpKennung)
      config = Config()

      where = "grpID = {0} and perArt in ('{1}','{2}')".format(gruppe.grpID,config.TN,config.FLI)
      self.viewhandler = Viewhandler(
         controller  = self,
         layout      = Viewhandler.GRID_LAYOUT,
         domain      = PersonDomain(db=self.db),
         where       = where,
         orderby     = 'perID',
         listparam   = {
            'db':self.db,
            'gruppe':gruppe
            },
         gridlist    = None,
         filter=Utility.normalizeFilter(self.cgiparam('_filter'))

         )

      # aufrufen des Handlers
      self.viewhandler.run()

Dieser Controller liefert eine vollständige Grid-Editiermaske:

BeispielGrid.jpg

Es stehen 3 Layouts zur Verfügung

PyUGAT: PymFrame (last edited 2013-01-24 11:23:16 by WilhelmNagy)