Datenbankanbindungen

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.