Explication à propos de l'initialisation/configuration de SQLAlchemy et Elixir dans Pylons
| Date: | 2009-03-11 |
|---|---|
| Authors: | Stéphane KLEIN <stephane at harobed.org> |
| Status: | Publié |
| Version: | 1.0 |
Voici dans ce billet quelques notes à propos de la configuration de Elixir dans Pylons "0.9.7". J'y ai aussi ajouté une "explication" personnelle de l'initialisation/configuration de SQLAlchemy dans Pylons.
1 SQLAlchemy
1.1 Généralités
L'initialisation de SQLAlchemy consiste par exemple à :
- créer un objet "metadata"
from sqlalchemy import MetaData
metadata = MetaData()
- créer un objet "session"
from sqlalchemy.orm import sessionmaker
session = sessionmaker()
- créer un objet "engine"
from sqlalchemy import create_engine
engine = create_engine('sqlite://') # in memory
- connecter la session à un "engine"
session.configure(bind = engine)
En résumé, il nous faut 3 éléments :
- un objet MetaData
- un objet Session
- un objet Engine
Nous retrouverons chacun de ces trois éléments dans les deux méthodes de configurations présentées ci-dessous.
1.2 Par défaut, comment Pylons initialise SQLAlchemy ?
Pylons défini quelques conventions quant à l'initialisation de SQLAlchemy.
Les fichiers en questions :
config/environment.py
model/__init__.py
model/meta.py
1.2.1 model/meta.py
Le module "model/meta.py" permet un accès standardisé aux variables "engine", "Session" et "metadata".
Extrait de quelques lignes du module "model/meta.py" :
from sqlalchemy import MetaData
engine = None
Session = None
metadata = MetaData()
1.2.2 model/__init__.py
Le module "model/__init__.py" se charge de l'initialisation de la couche modèle. Dans notre contexte (utilisation de SQLAlchemy) ce module se charge d'initialiser SQLAlchemy.
Extrait de quelques lignes du module "model/__init__.py" :
from sqlalchemy import orm
from pylonsapp.model import meta
def init_model(engine):
sm = orm.sessionmaker(autoflush=True, autocommit=False, bind=engine)
meta.engine = engine
meta.Session = orm.scoped_session(sm)
Ce module défini une fonction nommée "init_model" avec un seul paramètre nommé "engine". Cette fonction se charge de créer un objet Session et de connecter ce dernier avec l'objet engine passé en paramètre. Cette fonction est appelé par le module "config/environment.py".
1.2.3 config/environment.py
Pour finir, le module config/environment.py est chargé de la configuration globale du framework Pylons. Celle-ci s'effectue en fonction de paramètres définies dans un fichier ".ini". Ce fichier ".ini" est passé en paramètre lors du lancement de Pylons (par exemple : paster serve development.ini).
Extrait de quelques lignes du module "config/environment.py" :
from sqlalchemy import engine_from_config
...
from pylonsapp.model import init_model
def load_environment(global_conf, app_conf):
...
engine = engine_from_config(config, 'sqlalchemy.')
init_model(engine)
La fonction "engine_from_config" crée un objet Engine en fonction des paramètres présents dans le fichier ".ini".
1.2.4 Résumé
Pour résumer :
- model/meta.py : création de l'objet MetaData
- config/environment.py : création de l'objet Engine en fonction des paramètres définis dans un fichier ".ini"
- model/__init__.py : création de l'objet Session et connection de ce dernier avec l'objet engine créé dans environment.py
Note : par convention, model/__init__.py doit aussi se charger de l'initialisation des entités de la couche modèle.
2 Elixir
2.1 Configuration standard de Elixir
Voici la méthode standard d'initialisation de Elixir :
import elixir
elixir.metadata.bind = "sqlite:///movies.sqlite"
elixir.metadata.bind.echo = True
# ... déclaration des entités
elixir.setup_all()
Elixir met directement à disposition les objets suivants :
- Metadata : elixir.metadata
- Session : elixir.session
L'objet fournit par "elixir.metadata" est identique à un objet créé via "sqlalchemy.MetaData()".
Il est tout à fait possible de remplacer l'objet Session accessible via "elixir.session"... c'est ce qui va être fait dans la section suivante.
2.2 Initialiser Elixir dans Pylons
Voici maintenant comment configurer Elixir dans Pylons.
2.2.1 model/meta.py
Extrait de quelques lignes du module "model/meta.py" :
import elixir
engine = None
Session = None
metadata = elixir.metadata
Cette fois metadata contient l'objet Metadata défini par Elixir.
2.2.2 model/__init__.py
Extrait de quelques lignes du module "model/__init__.py" :
from sqlalchemy import orm
import elixir
from pylonsapp.model import meta
meta.Session = orm.scoped_session(orm.sessionmaker(autoflush=False, expire_on_commit=False))
elixir.options_defaults.update(dict(shortnames=True, inheritance='multi', polymorphic=True))
elixir.session = meta.Session
from pylonsapp.model.entities import *
def init_model(engine):
meta.metadata.bind = engine
meta.engine = engine
elixir.setup_all()
Explications du contenu/changement de ce module :
- cette fois, l'objet Session n'est pas créé dans la fonction "init_model" mais avant le chargement des classes "Entities"
- "elixir.options_defaults.update" permet de configurer certains "comportements" de Elixir
- le module "pylonsapp.model.entities" contient la déclaration des classes entities
- la fonction "init_model" permet de connecter l'objet Engine à l'objet Metadata créé par Elixir. Ensuite "elixir.setup_all()" permet l'initialisation des entities.
3 Modification de "lib/base.py"
Le billet Using Elixir with Pylons précise qu'il faut modifier le module "lib/base.py" afin de s'assurer du "nettoyage" de la Session de SQLAlchemy.
from pylonsapp.meta import Session
class BaseController(WSGIController):
def __call__(self, environ, start_response):
try:
return WSGIController.__call__(self, environ, start_response)
finally:
Session.remove()
4 Notes
- Cette configuration de Elixir permet toujours de créer des tables, classes... via les méthodes classiques de SQLAlchemy
- En cas du problème suivant : rien n'est enregistré lors de l'exécution de session.commit() c'est sans doute parce que les entities ont été sauvegardé dans une autre session.