Direkt zum Inhalt | Direkt zur Navigation

Benutzerspezifische Werkzeuge

Sie sind hier: Startseite / Tech-Blog

Tech-Blog

Technische Artikel zu Themen: Zope, Plone, Python, Pyramid, Django, Exim, Dovecot, Nagios, Shinken, Bacula und weiteren Open Source Themen...

Plone: Annotations zum speichern von Daten an Objekten

Annotations sind ein gutes Mittel um Komplexe Datenstrukturen an Objekten zu speichern, von denen das Objekt selbst keine Kenntnis haben muss.

Annotations werden in Zope üblicherweise als __annotations__ (BTree) unterhalb des betreffenden Objektes abgelegt. Das Annotations Objekt verhält sich wie ein Dictionary.

Um Konflikte mit anderen Anwendungen zu vermeiden, sollten Informationen unterhalb eines keys der den Namen der Anwendung enthält abgelegt werden.

Annotatable?

Damit annotations zum speichern von Daten, an einem Objekt verwendet werden können, muss das Objekt das Interface IAttributeAnnotatable implementiert haben. Um dies zu erreichen kann man es direkt in der Klasse implementieren oder wenn man selbst keinen Einfluss auf die Klasse hat, dann kann man es ganz einfach per zcml-Anweisung in der configure.zcml wie folgt definieren.

<class class="Products.CMFCore.MemberDataTool.MemberData">
  <implements
     interface="zope.annotation.interfaces.IAttributeAnnotatable"
     />
</class>

Daten in Annotations speichern

Wenn wir jetzt z.B. an ein MemberData Objekt Daten als annotations hängen möchten, dann können wir dies wie folgt machen.

from zope.annotation.interfaces import IAnnotations
from ZODB.PersistentMapping import PersistentMapping

admin = portal.portal_membership.getAuthenticatedMember()
admin_annotations = IAnnotations(admin)
infos = PersistentMapping({'groesse': '184cm'})
admin_annotations["member.datatest"] = infos

Das betreffende Objekt sollte persistent sein, was hier der Fall ist.

Weitere Informationen sind hier zu finden:

Plone: Password encrypt

Plone verschlüsselt Passwörter intern, im folgenden wird gezeigt, wie man die gleichen Mechanismen für eigene Funktionen verwenden kann.

Beispiel

Password verschlüsseln

from AccessControl import AuthEncoding

def _encryptPassword(self, pw):
    return AuthEncoding.pw_encrypt(pw, 'SSHA')

Password überprüfen

from AccessControl import AuthEncoding

AuthEncoding.pw_validate(saved_pw, entered_pw)

Plone: PloneFormGen readonly fields

Manchmal möchte man Felder nur anzeigen aber nicht änderbar machen. Nachfolgend wird eine Möglichkeit beschrieben, wie dies mit Hilfe von einem HiddenField und einem FomRichLabelField realisiert werden kann.

Ein verstecktes Feld anlegen

Wir legen ein FormStringField (Zeichenkettenfeld) an, füllen den Default wert mit dem Inhalt der angezeigt werden soll und markieren das Feld als versteckt.

Damit ist das Feld im Formular enthalten und wird beim versenden mit übertragen.

Ein Feld zur Anzeige des versteckten Feldes anlegen

Wir legen ein FormRichLabelField (Bezeichnungsfeld) an und geben in den overrides den folgenden TALES-Ausdruck ein:

string: <strong>My hidden field text:</strong> ${folder/myhiddenfield/getFgDefault}

Das Feld zeigt nun immer den Inhalt des Default-wertes des Feldes myhiddenfield an, kann aber selbst nicht editiert werden.

Text per CSS rotieren mit Compass

Im folgenden wird gezeigt, wie man mit dem CSS-Framework Compass, einen vertikalen Text erzeugt.

HTML Markup

<div id="box">
  <h1 id="parent-fieldname-title"
      class="documentFirstHeading">
    Spielplatzinspektion
  </h1>
</div>

Compass SCSS

@import "compass/css3/transform";

#box{
  width: 400px;
  height: 400px;
  border: 1px solid black;
  margin: 0 auto;
}
.verticalHeadline{
  font-size: 12pt;
  margin: 0;
  text-align: center;
  border: 1px solid green;
  @include rotate(270deg);
  @include apply-origin(top right, false);
  position: relative;
  right: 2em;
}

Resultat

vertical-headline

Plone: Diazo Theme & password_reset: Passwort setzen fehlgeschlagen

Wenn der password_reset in einer Plone Seite mit Diazo basiertem Theme nicht mehr funktioniert und statt dessen, die Meldung "Passwort setzen fehlgeschlagen" erscheint, liegt dies meist an einer fehlerhaften rules.xml.

Fehlendes base-Tag

Das Problem, im Theme fehlt das base-Tag welches Plone selbst rendert.

<base href="http://inqbus-hosting.de/" />

Dieses sollte mit folgender Regel ins Theme übertragen werden.

<drop theme="/html/head/title" />
<drop theme="/html/head/base" />
<drop theme="/html/head/comment()" />
<after
  content="/html/head/base | /html/head/comment() | /html/head/title"
  theme-children="/html/head"
  />

Plone: persistente Utilities manuell entfernen

In Plone werden von vielen Erweiterungen persistente Utilities (local utilities) registriert. Leider werden diese nicht bei allen Erweiterungen auch wieder entfernt, wenn man diese deinstalliert. Dies führt dazu, dass man diese Erweiterungen nicht mehr richtig entfernen kann. Nachfolgend wird erläutert, wie man local utilities manuell entfernt.

Wenn man eine solche fehlerhafte Erweiterung deinstalliert und diese auch aus dem Buildout entfernt, kommt es zu Fehlermeldungen der folgenden Art.

AttributeError: type object 'IWarehouseContainer' has no attribute '__iro__'

Um den Fehler zu beheben, müssen wir die Erweiterung noch einmal im Buildout installieren.

Registrierung finden

Am besten schauen wir uns als erstes den Code der Erweiterung an, der das local utility registriert. Hier zu suchen wir in dem package nach dem Interface 'IWarehouseContainer'.

~/.buildout/eggs/getpaid.warehouse-0.3-py2.7.egg/getpaid/warehouse
$ grep -r "IWarehouseContainer" .

Wir finden hier die Registrierung.

sm.registerUtility(component=warehouses, provided=interfaces.IWarehouseContainer )

Dies wird uns helfen das Utility zu entfernen.

Utility entfernen

Um das local utility zu entfernen starten wir die Instanz mit debug.

./bin/instance debug

Nun holen wir uns den site manager.

sm = app.Plone.getSiteManager()

Anschließend importieren wir das Interface IWarehouseContainer und holen uns das Utility.

from getpaid.warehouse.interfaces import IWarehouseContainer
warehouse = sm.queryUtility( IWarehouseContainer )

Das Utility können wir jetzt mit sm.unregisterUtility(IWarehouseContainer) aus der Registrierung entfernen. Da das Utility selbst IWarehouseContainer nicht implementiert, muss das Interface in diesem Fall mit angegeben werden.

sm.unregisterUtility(warehouse, provided=IWarehouseContainer)

Reguläre Ausdrücke in Python

Mit Regulären Ausdrücken (Regular Expressions) können Texte sehr flexibel und schnell verarbeitet werden. Da man in der Programmierung häufig auf Reguläre Ausdrück angewiesen ist, fasse ich hier ein paar Beispielhafte Lösungen zusammen.

Dokumentation zu regulären Ausdrücken in Python

Ich möchte hier nicht die vorhandene Dokumentation wiederholen, daher sei hiermit auf darauf verwiesen:

Beispiele

Zum testen von Regulären Ausdrücken eigent sich folgender Aufruf:

p = re.search(r'(?P<salutation>^Frau|^Herr)\s+(?P<name>.*)', "Herr Conrad Meier")

Hier wird das Modul re direkt verwendet, was zum testen ok ist, später ist es besser den regulären Ausdruck zu Kompelieren, damit die Operationen schneller ausgeführt werden können.

p = re.compile(r'(?P<salutation>^Frau|^Herr)\s+(?P<name>.*)')
p.search("Herr Conrad Meier")

Adressblock parsen

Wir wollen folgenden Adressblock parsen und Anrede, Name sowie PLZ und Ort auslesen.

ADDRESSBLOCK = u"""
  Inqbus GmbH & Co. KG
  Herr Maik Derstappen
  Geschäftsführer
  Karl-Heine-Str. 99
  04109 Leipzig
  Plagwitz
  Sachsen
  Deutschland"""

Wir definieren uns nun 2 reguläre Ausdrücke, die die Zeile mit der Anrede und dem Namen, sowie die Zeile mit der PLZ und dem Ort beschreiben. In den Ausdrücken werden benannte Guppen erzeugt, um auf diese später einfach zugreifen zu können.

z.B. erzeugt folgender Ausdruck eine Gruppe mit dem Namen salutation, die die Anrede (Frau/Herr) enthält.

(?P<salutation>^Frau|^Herr)
ReSalutationName = re.compile(r'(?P<salutation>^Frau|^Herr)\s+(?P<name>.*)')
ReZipecodeCity = re.compile(r'(?P<zipcode>\d{4,5})\s+(?P<city>.*)')

Im folgenden iterieren wir über die Zeilen des Adressblockes und wenden die regulären ausdrücke auf jede Zeile an. Wenn z.B. der erste Ausdruck ReSalutationName.search(line.strip()) ein Ergebnis zurück liefert, können wir auf die definierten Gruppen (salutation, name) per match_object.group('Gruppenname') zugreifen und bekommen den Teilstring der Gruppe zurück.

for line in ADDRESSBLOCK:
  salutation_name = ReSalutationName.search(line.strip())
  if salutation_name:
    salutation = salutation_name.group('salutation')
    name = salutation_name.group('name')
    continue

  zipcode_city = ReZipecodeCity.search(line.strip())
  if zipcode_city:
    zipcode = zipcode_city.group('zipcode')
    city = zipcode_city.group('city')
    continue

How to install MySQLdb in virtualenv on Debian

To install MySQL-Python with pip, you need some development header files of libmysqlclient.

If you try to install MySQL-Python with pip as follow:

 

pip install MySQL-Python

 

You will get this error, because of missing header files of libmysqlclient:

$ pip install MySQL-Python
Downloading/unpacking MySQL-Python 
Downloading MySQL-python-1.2.3.tar.gz (70Kb): 70Kb downloaded 
Running setup.py egg_info for package MySQL-Python   
sh: 1: mysql_config: not found   
Traceback (most recent call last):     
File "<string>", line 14, in <module>     
File "/home/maik/.virtualenvs/env1/build/MySQL-Python/setup.py", line 15, in <module>       
metadata, options = get_config()     
File "setup_posix.py", line 43, in get_config       
libs = mysql_config("libs_r")     
File "setup_posix.py", line 24, in mysql_config       
raise EnvironmentError("%s not found" % (mysql_config.path,))   
EnvironmentError: mysql_config not found   
Complete output from command python setup.py egg_info:   
sh: 1: mysql_config: not found
Traceback (most recent call last):
File "<string>", line 14, in <module>
File "/home/maik/.virtualenvs/env1/build/MySQL-Python/setup.py", line 15, in <module>
metadata, options = get_config()
File "setup_posix.py", line 43, in get_config
libs = mysql_config("libs_r")
File "setup_posix.py", line 24, in mysql_config
raise EnvironmentError("%s not found" % (mysql_config.path,))
EnvironmentError: mysql_config not found

This error can be solved by installing the libmysqlclient header files.

aptitude install libmysqlclient-dev