Package: gino-enum-tables
This is a port ofSqlAlchemy Enum Tablesto supportGINO ormSQLAlchemy has built-inenum.Enumsupport,
via its column typesqlalchemy.Enum.
However, this type relies either on the backend’s enum type,
or on a check constraints. Both of these are immutable objects,
which are a pain in the butt to modify
(only PostgreSQL supports adding values to an enum type,
and even then it doesn’t support removing them).Another often-used pattern to support enums in a database
is via a dedicated table that reflects the enum values.
This requires updating the table everytime the enum is modified,
but doing so is much simpler than replacing a type.This package allows you to create the enum table,
and columns referencing that table, directly from
a Python enum class. It also interfaces with Alembic
to automatically addINSERTandDELETEstatements
to your autogenerated migration scripts.When to useOnly works with Pythons’s enumeration classes,
or at least one with a behavior similar toenum.Enum.
Does not work with collections of arbitrary entries.Better used for frequently updated enumeration classes.Do notuse with another package that providesop.enum_insertandop.enum_deleteoperations in Alembic.Installationpip install gino-enum-tablesHow to use with GINOimport enum

# Gino instance
from . import db

import enumtables as et

# Create the Python enumeration class
class MyEnum(enum.Enum):
    HELLO = "HELLO"
    WORLD = "WORLD"

# Create the enumeration table
# Pass your enum class and the GINO base model to enumtables.EnumTable
MyEnumTable = et.EnumTable(MyEnum, db.Model)

# Create a model class that uses the enum
class MyModel(Base):
    __tablename__ = "my_model"
    # Pass the enum table (not the enum class) to enumtables.EnumType
    # Add the foreign key to the enum table
    enum_value = db.Column(et.EnumType(MyEnumTable), db.ForeignKey('my_enum_table.item_id') primary_key = True)

    # When valued (on an instance of MyModel), enum_value will be an instance of MyEnum.First, theEnumTablefactory takes the enum class and the base GINO model class
to create the actual ORM class. Then this ORM class is passed to theEnumTypecustom type class, along with a ForeignKey to the enum table,
to create the SQLAlchemy column linked to the enum table.
The column behaves just as if it had SqlAlchemy’s ownEnumtype.Note that the ForeignKey points to ‘my_enum_table.item_id’. If the tablename argument isn’t passed to EnumTable, the table name is created from the Enum name by doing a camelCase to snake_case conversion. ‘item_id’ is the primary key of the enum table (my_enum_table) holding enum values.On the implementation side,EnumTableis not a class,
it’s a factory function that performs Python black magic
to create a subclass of the declarative base, and set it up to be a DB table
containing the enum items (actually it just has one columnitem_idof type String).How to use with AlembicFirst add:from enumtables import alembic_opsat the begining of yourenv.pyfile,
then add the same line in the imports of yourscript.py.makofile.
The package uses Alembic’s standard hooks to take care of migration generation.Don’t forget to review the migrations afterwards.
Especially make sure that, if the table did not exist before,
theop.enum_insertcommands are locatedafterthe correspondingop.create_tablecommand.Other usesUsing the enum table class directlyThe enum table class behaves like any SqlAlchemy ORM class:enum_query = session.query(MyEnumTable)
result = enum_query.first()

# The column item_id stores the name of the enum item as a string
enum_name = result.item_idAdding more columns to the enum tablesAny keyword argument passed to theEnumTablefactory becomes a member of the table class.
Which means, you can pass anything (like a column) exactly as you would defined a usual ORM class:BetterEnumTable = et.EnumTable(
    MyEnum,
            Base,

    # tablename is turned into __tablename__
    tablename = "better_enum",

    # Let's add a new column!
    order = sa.Column(sa.Integer, nullable = False),

    # And since it's an ordering number, let's make it unique too.
    __table_args__ = (
        sa.UniqueConstraint('order'),
    ),
)