如何将 SQLAlchemy 中的 Enum 映射到具有特殊字符的列?

huangapple go评论90阅读模式
英文:

How do I map Enum with SQLAlchemy to a Column that has special characters?

问题

我可以帮你解释这个问题并提供一些建议。问题出在你试图在 SQLAlchemy 中使用 enum 类型时,数据库接受的值与 Python Enum 中的值之间的不匹配。为了解决这个问题,你可以尝试以下步骤:

首先,你可以使用 SQLAlchemy 的 server_default 参数,以确保在插入数据时,Enum 值与数据库中的定义匹配。在你的 SQLAlchemy 模型中,尝试将 server_default 添加到 type 列上:

type = Column("type", Enum(TypeEnum), server_default=TypeEnum.Hired)

这将在插入数据时将默认值设置为 Hired,以确保匹配数据库中的定义。

接下来,你应该使用 Python Enum 值而不是字符串值,以确保与数据库中的定义匹配。在读取 JSON 数据并将其转换为模型实例之前,确保你的 StatusSchema 中的 type 属性使用 Python Enum 值。例如:

from your_module import TypeEnumSchema

class StatusSchema(BaseModel):
    id: str
    type: TypeEnumSchema  # values: TypeEnum.Hired, TypeEnum.Nonhired

这将确保你的数据与数据库中的 Enum 值匹配。

最后,检查你的 JSON 数据文件,确保它使用正确的 Python Enum 值。例如:

{
    "data": [
        {
            "id": "63dd55b2-a99f-4e7b-985f-22c7bf80ab42",
            "type": "hired"
        },
        {
            "id": "41f98875-06c7-4cb1-b3d0-07f7ae192c0c",
            "type": "non-hired"
        }
    ]
}

确保在 JSON 文件中使用 Python Enum 值而不是字符串值。

通过采取这些步骤,你应该能够确保 Python Enum 值与数据库 Enum 值匹配,并且在将数据插入数据库时不会出现错误。

英文:

I have a database (in Postgresql) that has a table with a column of type enum. This enums receive only the values of 'Hired', 'Possible Hire' and 'Non-hired'.

The DDL would be:

CREATE TYPE enum_type AS ENUM('Hired', 'Possible Hire', 'Non-hired');

CREATE TABLE Status (
    ID VARCHAR(63) PRIMARY KEY,
    type enum_type NOT NULL
)

The class Enum in Python:

import enum

class TypeEnum(str, enum.Enum):
    Hired = 'hired'
    PossibleHire = 'possible hire'
    Nonhired = 'non-hired'

And the SQLAlchemy model:

from sqlalchemy import Column, Enum, String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class StatusModel(Base):
    __tablename__ = "status"

    id = Column(String(63), primary_key=True, index=True)
    type = Column("type", Enum(TypeEnum))

But the problem is that I can't put 'Non-hired' in the variable name of a class.

I researched and found this article: https://www.notinventedhere.org/articles/python/how-to-use-strings-as-name-aliases-in-python-enums.html

That says that you need to use functional API of Enum.

So, I wrote the code:

TypeEnum = enum.Enum(
    value="TypeEnum",
    names=[
        ('Hired', 'hired'),
        ('Possible hire', 'possible hire'),
        ('Non-hired', 'non-hired'),
    ]
)

But with this functional syntax, the SQLAlchemy just passes the value of enum. For example, when StatusModel is initialized, I put 'hired' in the type and in Session.add arrives 'hired' instead of 'Hired'. In other words: If I pass "hired" it is rejected by the database as an invalid value for the enum. SQLAlchemy never interferes with the value being passed. Maybe the problem is how I'm passing the arguments in the column of the table? type = Column("type", Enum(TypeEnum))

I want to do an update to the problem:

I passed it the same Enum but from a schema in Pydantic

from enum import Enum
from pydantic import BaseModel

TypeEnumSchema = Enum(
value="TypeEnum",
names=[
   ('Hired', 'hired'),
   ('PossibleHire', 'possible hire'),
   ('Non-hired', 'non-hired'),
  ]
)

class StatusSchema(BaseModel):
id: str
type: TypeEnumSchema  # values: hired, non-hired
class Config:
    orm_mode = True

The data flow is:

I have this status_mock.json:


{
    "data": [
      {
        "id": "63dd55b2-a99f-4e7b-985f-22c7bf80ab42",
        "type": "hired"
      },
      {
        "id": "41f98875-06c7-4cb1-b3d0-07f7ae192c0c",
        "type": "non-hired"
      },
    ]
}

Then I read it and take the data key:

data_object = read_json('./status_mock.json')["data"]

I get the list of data, pass each item to the schema and then to the model:


list_of_data: list[StatusSchema] = [StatusSchema(**item) for item in data_object]

records = StatusModel(id=data.id, type=data.type)

And then I write the code.


db.bulk_data(records)

def bulk_data(self, data):
        with Session(self.engine) as session:
            session.add_all(data)
            session.commit()

But now it generates the next error:

sqlalchemy.exc.StatementError: (builtins.LookupError) 'TypeEnum.Hired' is not among the defined enum values. Enum name: typeenum. Possible values: Hired, Possible hired, Non-hired

答案1

得分: 0

将SQLAlchemy模型从以下功能性语法更改为:

class StatusModel(Base):
    __tablename__ = "status"

    id = Column(String(63), primary_key=True, index=True)
    type = Column("type", Enum(TypeEnum))

更改为:


class StatusModel(Base):
    __tablename__ = "status"

    id = Column(String(63), primary_key=True, index=True)
    type = Column("type", Enum(TypeEnum).value_callables)

在Enum()中添加.value_callables

英文:

Basically, with the functional syntax I had to change the SQLAlchemy model from:

class StatusModel(Base):
    __tablename__ = "status"

    id = Column(String(63), primary_key=True, index=True)
    type = Column("type", Enum(TypeEnum))

To:


class StatusModel(Base):
    __tablename__ = "status"

    id = Column(String(63), primary_key=True, index=True)
    type = Column("type", Enum(TypeEnum).value_callables)

Adding .value_callables to the Enum()

huangapple
  • 本文由 发表于 2023年3月12日 12:47:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/75711096.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定