Skip to content
Advertisement

SQLAlchemy Enum type field with integer value

There is an events model in which I want to add a memebers_count field to indicate the number of participants. The user will make a single selection from the select form. I am using Enum type for this. The model looks like this

class MembersQuantity(enum.Enum):
    two = 2
    three = 3
    four = 4
    five = 5
    six = 6
    ...
    nineteen = 19
    twenty = 20
    thirty = 30
    forty = 40
    fifty = 50
    unlimited = 1000

events = Table(
    "events",
    metadata,
    Column("id", Integer(), primary_key=True),
    ...
    Column("members_count", Enum(MembersQuantity, values_callable=lambda obj: [e.value for e in obj]),
           nullable=False,
           default=MembersQuantity.two.value,
           server_default=MembersQuantity.two.value),
    ....

An error appears in the console when this entry is made

  File "/home/jekson/Projects/own/wplay/./models/events.py", line 64, in <module>
    Column("members_count", Enum(MembersQuantity, values_callable=lambda obj: [e.value for e in obj]),
  File "<string>", line 2, in __init__
  File "/home/jekson/virtualenvs/wplay/lib/python3.9/site-packages/sqlalchemy/util/deprecations.py", line 139, in warned
    return fn(*args, **kwargs)
  File "/home/jekson/virtualenvs/wplay/lib/python3.9/site-packages/sqlalchemy/sql/sqltypes.py", line 1383, in __init__
    self._enum_init(enums, kw)
  File "/home/jekson/virtualenvs/wplay/lib/python3.9/site-packages/sqlalchemy/sql/sqltypes.py", line 1425, in _enum_init
    length = max(len(x) for x in self.enums)
  File "/home/jekson/virtualenvs/wplay/lib/python3.9/site-packages/sqlalchemy/sql/sqltypes.py", line 1425, in <genexpr>
    length = max(len(x) for x in self.enums)
TypeError: object of type 'int' has no len()

I assumed the problem was that [e.value for e in obj] should be str instead of int and replaced it with [str(e.value) for e in obj] but got another error

sqlalchemy.exc.ArgumentError: Argument 'arg' is expected to be of the type '<class 'str'>' or '<class 'sqlalchemy.sql.elements.ClauseElement'>' or '<class 'sqlalchemy.sql.elements.TextClause'>', got '<class 'int'>'

What’s wrong?

Advertisement

Answer

The question is what do you want to persist? two or 2 or <MembersQuantity.two: 2>?

SQLAlchemy docs states:

Above, the string names of each element, e.g. “one”, “two”, “three”, are persisted to the database; 
the values of the Python Enum, here indicated as integers, are not used;
the value of each enum can therefore be any kind of Python object whether or not it is persistable.

So if you have integer values of enum, you probably should have something like this:

    Column(
       "members_count", 
       Enum(MembersQuantity),
       nullable=False,
       default=MembersQuantity.two.name,
       server_default=MembersQuantity.two.name
    )

The name attribute is two, while value attribute is 2 in your case. But you persist only left-hand side strings. You should use values_callable only if you want to persist values instead of names, but docs states also: value_callables - a callable which will be passed the PEP-435 compliant enumerated type, which should then return a list of string values to be persisted. So you can persist value only if it is a string.

Here is a topic that can be helpful: How to use Enum with SQLAlchemy and Alembic?

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement