Source code for assembl.models.vocabulary
from sqlalchemy import (
Column,
Integer,
String,
ForeignKey,
)
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declared_attr
from . import Base, DeclarativeAbstractMeta
from .langstrings import LangString
from ..lib.logging import getLogger
from ..lib.decl_enums import UpdatablePgEnum
[docs]class AbstractVocabulary(Base):
__metaclass__ = DeclarativeAbstractMeta
__abstract__ = True
"""A vocabulary backed by some identifier"""
@declared_attr
def name_id(cls):
return Column("name_id", Integer, ForeignKey(LangString.id))
@declared_attr
def name(cls):
return relationship(
LangString,
lazy="joined", single_parent=True,
primaryjoin=cls.name_id == LangString.id,
backref=backref("idvocabulary_from_name", lazy="dynamic"),
cascade="all, delete-orphan")
@classmethod
def populate_db(cls, db=None):
db = db or cls.default_db
initial_names = getattr(cls, "_initial_names", None)
if initial_names:
values = db.query(cls).filter(cls.id.in_(cls.Enum.__members__.values())).all()
values = {v.id: v for v in values}
for id, names in initial_names.items():
value = values.get(id, None)
if value is None:
value = cls(id=id, name=LangString())
db.add(value)
if value.name is None:
value.name = LangString()
existing = {e.locale for e in value.name.entries}
for locale, val in names.items():
if locale not in existing:
value.name.add_value(val, locale)
# Remember to LangString.setup_ownership_load_event on concrete subclasses
[docs]class AbstractIdentifierVocabulary(AbstractVocabulary):
"""A vocabulary backed by a string"""
__abstract__ = True
id = Column(String, primary_key=True)
[docs]class AbstractEnumVocabulary(AbstractVocabulary):
"""A vocabulary backed by a Python Enum.
Define an 'Enum' member class (derived from enum.Enum)
in concret subclasses.
"""
__abstract__ = True
@declared_attr
def pg_enum_name(cls):
return cls.__tablename__ + '_type'
@declared_attr
def pg_enum(cls):
# TODO: reify
return UpdatablePgEnum(
cls.Enum, name=cls.pg_enum_name,
metadata=cls.metadata, create_type=True)
@declared_attr
def id(cls):
return Column(cls.pg_enum, primary_key=True)
@declared_attr
def name_id(cls):
return Column("name_id", Integer, ForeignKey(LangString.id))
@declared_attr
def name(cls):
return relationship(
LangString,
lazy="joined", single_parent=True,
primaryjoin=cls.name_id == LangString.id,
backref=backref("voc_%s_from_name" % (cls.__tablename__,),
lazy="dynamic"),
cascade="all, delete-orphan")
@classmethod
def populate_db(cls, db=None):
db = db or cls.default_db
cls.pg_enum.create(db.bind)
super(AbstractEnumVocabulary, cls).populate_db(db)
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self.id.name)