SQLAlchemy bietet standardisierte Interaktionsmuster zur Erstellung von Engines, Metadata, Tabellen und Mapper. Dabei ist zu beachten, dass
- verschiedene Produkte ihre eigenen Datenbankverbindungen aufbauen;
- jede geteilte Datenbankquelle thread-safe ist;
- Datenbanktransaktionen mit Zope-Transaktionen synchronisiert werden;
- Data Source names (DSN) zur Laufzeit nicht bekannt sind.
collective.lead bietet eine Basisklasse zur Erstellung von Hilfsmethoden, die Verbindungseinstellungen, Tabellen und Mapper kapseln. Damit wir dies in unseren Hilfsmethoden vs.registrations und vs.reservations verwenden können, wird collective.lead als Abhängigkeit in vs.registration/setup.py eingetragen:
install_requires=[ 'setuptools', # -*- Extra requirements: -*- 'MySQL-python', 'collective.lead>=1.0b3,<2.0dev', ],
Bei einem Aufruf von ./bin/buildout sollte nun collective.lead mitinstalliert werden, welches dann die letzte unterstützte Version von SQLAlchemy installiert. Daneben benötigen wir noch das MySQL-python-Paket, welches MySQL-Treiber für Python bereitstellt.
Das Datenbank-Hilfsprogramm selbst wird dann in vs.registration/vs/registration/db.py erstellt. Die Datei enthält ebenfalls die Implementierung von IDatabaseSettings, eine persistente, lokale Hilfsmethode zum Speichern der Verbindungseinstellungen:
from persistent import Persistent from zope.interface import implements from zope.component import getUtility from collective.lead import Database from vs.registration.interfaces import IDatabaseSettings from sqlalchemy.engine.url import URL from sqlalchemy import Table, mapper, relation from vs.registration.occurrence import Occurrence from vs.registration.reservation import Reservation class ReservationsDatabaseSettings(Persistent): implements(IDatabaseSettings) drivername = 'mysql' hostname = 'localhost' port = None username = '' password = None database = '' class ReservationsDatabase(Database): @property def _url(self): settings = getUtility(IDatabaseSettings) return URL(drivername=settings.drivername, username=settings.username, password=settings.password, host=settings.hostname, port=settings.port, database=settings.database) def _setup_tables(self, metadata, tables): tables['occurrence'] = Table('occurrence', metadata, autoload=True) tables['reservation'] = Table('reservation', metadata, autoload=True) def _setup_mappers(self, tables, mappers): mappers['occurrence'] = mapper(Occurrence, tables['occurrence']) mappers['reservation'] = mapper(Reservation, tables['reservation'], properties = { 'occurrence' : relation(Occurrence), })
Die collective.lead.Database-Klasse erlaubt uns, nur wenige Eigenschaften anzugeben, um eine Datenbankverbindung, Tabellen und Mapper zu erstellen.
Nun wird die ReservationsDatabase-Methode noch in vs.registration/vs/registration/configure.zcml registriert:
<utility provides="collective.lead.interfaces.IDatabase" factory=".db.ReservationsDatabase" name="vs.reservations" />
Da die lokale ReservationsDatabaseSettings-Hilfsmethode jedoch erst mit der Installation des Produkts registriert werden muss, kann das Generic Setup-Profil vs.registration/vs/registration/profiles/default/componentregistry.xml hierfür verwendet werden:
<?xml version="1.0"?> <componentregistry> <utilities> <utility interface="vs.registration.interfaces.IDatabaseSettings" factory="vs.registration.db.ReservationsDatabaseSettings" /> </utilities> </componentregistry>
Nun sollte auf die Datenbank zugegriffen werden können mit:
>>> from zope.component import getUtility >>> from collective.lead.interfaces import IDatabase >>> db = getUtility(IDatabase, name='vs.reservations')
Dem db-Objekt stehen anschließend die Eigenschaften von collective.lead.interfaces.IDatabase zur Verfügung.