You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
5.5 KiB
181 lines
5.5 KiB
""" sqlalchemy metadata configuration """ |
|
|
|
from datetime import datetime |
|
|
|
import zope.sqlalchemy |
|
from sqlalchemy import ( |
|
Enum, |
|
Text, |
|
Float, |
|
Table, |
|
Column, |
|
Integer, |
|
DateTime, |
|
ForeignKey, |
|
engine_from_config, |
|
) |
|
from sqlalchemy.orm import mapper, relationship, sessionmaker |
|
from sqlalchemy.schema import MetaData |
|
|
|
from . import repo, models |
|
|
|
# Recommended naming convention used by Alembic, as various different database |
|
# providers will autogenerate vastly different names making migrations more |
|
# difficult. See: http://alembic.zzzcomputing.com/en/latest/naming.html |
|
NAMING_CONVENTION = { |
|
"ix": "ix_%(column_0_label)s", |
|
"uq": "uq_%(table_name)s_%(column_0_name)s", |
|
"ck": "ck_%(table_name)s_%(constraint_name)s", |
|
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", |
|
"pk": "pk_%(table_name)s", |
|
} |
|
|
|
metadata = MetaData(naming_convention=NAMING_CONVENTION) |
|
|
|
order_table = Table( |
|
"orders", |
|
metadata, |
|
Column("id", Integer, primary_key=True), |
|
Column("cas_description", Text, nullable=False), |
|
Column("catalog_nr", Text, nullable=False), |
|
Column("vendor", Text, nullable=False), |
|
Column("category", Enum(models.OrderCategory), nullable=False), |
|
Column("package_size", Text, nullable=False), |
|
Column("unit_price", Float, nullable=False), |
|
Column("currency", Text, nullable=False, default="EUR"), |
|
Column("amount", Integer, nullable=False), |
|
Column("account", Text, nullable=False, default=""), |
|
Column("comment", Text, nullable=False, default=""), |
|
# redundant properties, could be determined from orders log |
|
Column("created_on", DateTime, nullable=False, default=datetime.utcnow), |
|
Column("created_by", Text, nullable=True, index=True), |
|
Column("status", Enum(models.OrderStatus), nullable=True), |
|
) |
|
|
|
|
|
log_table = Table( |
|
"logs", |
|
metadata, |
|
Column("order_id", Integer, ForeignKey("orders.id"), primary_key=True), |
|
Column("status", Enum(models.OrderStatus), primary_key=True), |
|
Column("date", DateTime, primary_key=True, default=datetime.utcnow), |
|
Column("by", Text, nullable=False), |
|
) |
|
|
|
vendor_table = Table( |
|
"vendors", |
|
metadata, |
|
Column("term", Text, primary_key=True), |
|
Column("name", Text, index=True), |
|
) |
|
|
|
user_table = Table( |
|
"users", |
|
metadata, |
|
Column("id", Integer, primary_key=True), |
|
Column("username", Text, nullable=False, index=True), |
|
Column("first_name", Text, nullable=False), |
|
Column("last_name", Text, nullable=False), |
|
Column("email", Text, nullable=False, index=True), |
|
Column("password", Text, nullable=False), |
|
Column("role", Enum(models.UserRole)), |
|
) |
|
|
|
reset_token_table = Table( |
|
"reset_tokens", |
|
metadata, |
|
Column("token", Text, primary_key=True), |
|
Column("user_id", Integer, nullable=False), |
|
Column("valid_until", DateTime, nullable=False), |
|
) |
|
|
|
|
|
def start_mappers(): |
|
""" maps data base tables to model objects """ |
|
mapper( |
|
models.OrderItem, |
|
order_table, |
|
properties={ |
|
"log": relationship( |
|
models.LogEntry, backref="order", order_by=log_table.c.date |
|
) |
|
}, |
|
) |
|
mapper(models.LogEntry, log_table) |
|
mapper(models.Vendor, vendor_table) |
|
mapper(models.User, user_table) |
|
mapper(models.PasswordResetToken, reset_token_table) |
|
|
|
|
|
def get_engine(settings, prefix="sqlalchemy."): |
|
""" returns a sqlalchemy engine from a settings dict """ |
|
return engine_from_config(settings, prefix) |
|
|
|
|
|
def get_session_factory(engine): |
|
""" returns a sqlalchemy session factory for a db enging """ |
|
factory = sessionmaker() |
|
factory.configure(bind=engine) |
|
return factory |
|
|
|
|
|
def get_tm_session(session_factory, transaction_manager): |
|
""" |
|
Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. |
|
|
|
This function will hook the session to the transaction manager which |
|
will take care of committing any changes. |
|
|
|
- When using pyramid_tm it will automatically be committed or aborted |
|
depending on whether an exception is raised. |
|
|
|
- When using scripts you should wrap the session in a manager yourself. |
|
For example:: |
|
|
|
import transaction |
|
|
|
engine = get_engine(settings) |
|
session_factory = get_session_factory(engine) |
|
with transaction.manager: |
|
dbsession = get_tm_session(session_factory, transaction.manager) |
|
|
|
""" |
|
dbsession = session_factory() |
|
zope.sqlalchemy.register( |
|
dbsession, transaction_manager=transaction_manager |
|
) |
|
return dbsession |
|
|
|
|
|
def get_repo_with_session(session_factory, request): |
|
""" returns an sql alchemy repository with database session configured """ |
|
# request.tm is the transaction manager used by pyramid_tm |
|
session = get_tm_session(session_factory, request.tm) |
|
return repo.SqlAlchemyRepository(session) |
|
|
|
|
|
def includeme(config): |
|
""" |
|
Initialize the model for a Pyramid app. |
|
|
|
Activate this setup using ``config.include('ordr3.adapters')``. |
|
|
|
""" |
|
settings = config.get_settings() |
|
settings["tm.manager_hook"] = "pyramid_tm.explicit_manager" |
|
|
|
# use pyramid_tm to hook the transaction lifecycle to the request |
|
config.include("pyramid_tm") |
|
|
|
# use pyramid_retry to retry a request when transient exceptions occur |
|
config.include("pyramid_retry") |
|
|
|
session_factory = get_session_factory(get_engine(settings)) |
|
config.registry["dbsession_factory"] = session_factory |
|
|
|
# make request.dbsession available for use in Pyramid |
|
config.add_request_method( |
|
lambda r: get_repo_with_session(session_factory, r), "repo", reify=True |
|
) |
|
|
|
start_mappers()
|
|
|