Skip to main content

Support for storing enum values in a table instead of a type

Project description

This is a port of SqlAlchemy Enum Tables to support GINO orm

SQLAlchemy has built-in enum.Enum support, via its column type sqlalchemy.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 add INSERT and DELETE statements to your autogenerated migration scripts.

When to use

  1. Only works with Pythons’s enumeration classes, or at least one with a behavior similar to enum.Enum. Does not work with collections of arbitrary entries.

  2. Better used for frequently updated enumeration classes.

  3. Do not use with another package that provides op.enum_insert and op.enum_delete operations in Alembic.

Installation

pip install gino-enum-tables

How to use with GINO

import 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, the EnumTable factory takes the enum class and the base GINO model class to create the actual ORM class. Then this ORM class is passed to the EnumType custom 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 own Enum type.

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, EnumTable is 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 column item_id of type String).

How to use with Alembic

First add:

from enumtables import alembic_ops

at the begining of your env.py file, then add the same line in the imports of your script.py.mako file. 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, the op.enum_insert commands are located after the corresponding op.create_table command.

Other uses

Using the enum table class directly

The 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_id

Adding more columns to the enum tables

Any keyword argument passed to the EnumTable factory 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'),
    ),
)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gino-enum-tables-1.0.7.tar.gz (10.3 kB view details)

Uploaded Source

Built Distribution

gino_enum_tables-1.0.7-py2.py3-none-any.whl (10.6 kB view details)

Uploaded Python 2 Python 3

File details

Details for the file gino-enum-tables-1.0.7.tar.gz.

File metadata

  • Download URL: gino-enum-tables-1.0.7.tar.gz
  • Upload date:
  • Size: 10.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.0.10 CPython/3.8.5 Linux/5.8.1-arch1-1

File hashes

Hashes for gino-enum-tables-1.0.7.tar.gz
Algorithm Hash digest
SHA256 89b22411c51ff2504641384ede126fa5478757693f3aaaa839310bd36f863a7e
MD5 94f467d90b7897f94a724d83621a251a
BLAKE2b-256 e4396fe2aa09dd57bb2699c447a7dd71e00355d1ad41a2a01d0c75cc4e380cf4

See more details on using hashes here.

File details

Details for the file gino_enum_tables-1.0.7-py2.py3-none-any.whl.

File metadata

File hashes

Hashes for gino_enum_tables-1.0.7-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 0a66e5ebb22fb860525ec80c6f10cef540f9b10bf7ee27663bb1179898e4f8ba
MD5 2f0d044157215b8c1e408e636f40cbda
BLAKE2b-256 04a2ad2bf895636d4842936158bc943f3a11372df9c880fcc35f234fa80d073f

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page