SQLAlchemy create_all() не создает таблицы

Я пытаюсь создать набор таблиц, используя SQLAlchemy 2.0.

import logging
import logging.handlers

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(name)-5s] [%(levelname)-3s] %(message)s'
)

from typing import List, Dict
# from DAL.database import Report, Database
import util.utility as util

import sqlalchemy
from sqlalchemy import MetaData, DateTime, func, exc
from sqlalchemy.engine import Engine
from sqlalchemy.orm import relationship, sessionmaker, Mapped, DeclarativeBase, mapped_column
from datetime import datetime

config: Dict = util.read_config("../static/configurations/custom.yaml")

class Base(DeclarativeBase):
    pass


class Attribute_Details(Base):
    """

    """
    __tablename__ = "attribute_details"
    id: Mapped[int] = mapped_column(primary_key=True)
    description: Mapped[str] = mapped_column()
    opened: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    closed: Mapped[datetime] = mapped_column()
    report_attr: Mapped["Report_Attributes"] = relationship(back_populates = "details")


class Database:
    """
    Abstraction layer for the SQLAlchemy db interface
    """

    meta: MetaData
    engine: Engine
    session_maker: sessionmaker
    __connected: bool = False

    tables: List[Base] = [Attribute_Details]

    def __init__(self, con_str: str):
        self.logger = logging.getLogger(Database.__name__)
        self.logger.info("Connecting to DB: {}".format(con_str))
    
        self.db = sqlalchemy.create_engine(con_str, echo=False)

    def connect(self) -> bool:
        """
        Establish connection to the database
        """
        try:
            self.engine = self.db.connect()
            self.session_maker = sessionmaker(bind=self.engine)
            self.meta = sqlalchemy.MetaData()
            return True
        except exc.SQLAlchemyError as e:
            print(str(e))
            return False


    def create_tables(self):
        """
        Create the corresponding tables for the Report template
        """
        for table in self.tables:
            if not self.engine.dialect.has_table(self.engine, table.__tablename__):
                table_obj = [table.__table__]
                Base.metadata.create_all(self.engine, tables=table_obj)
                # CreateTable(table_obj).compile(dialect=postgresql.dialect())
                self.logger.info("Created DB Table {}".format(table.__tablename__))
            else:
                self.logger.info("DB Table {} already exists".format(table.__tablename__))


db = Database(config['db_con_test'])
db.connect()
db.create_tables()

Хотя он сообщает «Создана таблица БД атрибут_детали», таблица не создается в базе данных. Я попробовал вариант с Base.metadata.create_all(), а также CreateTable(). Я не могу обнаружить проблему, поскольку создание таблиц с помощью нового декларативного сопоставления SQL2.0 не очень хорошо объяснено... по крайней мере, для меня.

Пожалуйста, отредактируйте вопрос, чтобы предоставить минимально воспроизводимый пример. Например, connect и create_tables — это методы класса, но в вашем вопросе их нет в классе. После внесения изменений, необходимых для запуска кода, я не смог воспроизвести проблему.

Henry 06.06.2024 15:58

Обновил код до полного MVE (кроме того, что вам нужна база данных postgresql), но таблица по-прежнему не создается. Есть ли способ получить дополнительную информацию о причине, по которой Алхимия не создала таблицу?

po.pe 06.06.2024 16:10

Ваш self.engine не является объектом Engine, это объект Connection. Очевидно, когда .create_all() передается соединение, оно предполагает, что вы приняли на себя ответственность за обработку транзакций, поэтому он выдает оператор CREATE TABLE для этого соединения, но не фиксирует (потому что это ваша работа). Я бы посоветовал вам не использовать имена self.db для вашего объекта Engine и self.engine для вашего объекта Connection — это слишком запутанно.

Gord Thompson 06.06.2024 18:10

«Есть ли способ получить больше информации о причине, по которой Алхимия не создала стол?» — Использование echo=True или echo = "debug" при вызове create_engine() часто бывает полезным.

Gord Thompson 06.06.2024 18:24

@GordThompson, ты хочешь опубликовать ответ? Ваше мнение относительно перепутанного соединения и двигателя было правильным.

po.pe 12.06.2024 10:23
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
5
66
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Типичное использование — вызов .create_all() с объектом Engine.

from sqlalchemy import create_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column


class Base(DeclarativeBase):
    pass


class Thing(Base):
    __tablename__ = "thing"
    id: Mapped[int] = mapped_column(primary_key=True)
    description: Mapped[str]


engine = create_engine("sqlite://", echo=True)
Base.metadata.create_all(engine)
"""
CREATE TABLE thing (
    id INTEGER NOT NULL, 
    description VARCHAR NOT NULL, 
    PRIMARY KEY (id)
)


2024-06-12 06:37:19,592 INFO sqlalchemy.engine.Engine [no key 0.00018s] ()
2024-06-12 06:37:19,592 INFO sqlalchemy.engine.Engine COMMIT

Process finished with exit code 0
"""

Очевидно, когда .create_all() передается соединение, оно предполагает, что вы приняли на себя ответственность за обработку транзакций, поэтому он выдает оператор CREATE TABLE для этого соединения, но не фиксирует (потому что это ваша работа).

engine = create_engine("sqlite://", echo=True)
conn = engine.connect()
Base.metadata.create_all(conn)
"""
CREATE TABLE thing (
    id INTEGER NOT NULL, 
    description VARCHAR NOT NULL, 
    PRIMARY KEY (id)
)


2024-06-12 06:42:01,819 INFO sqlalchemy.engine.Engine [no key 0.00024s] ()

Process finished with exit code 0
"""

Однако если мы используем менеджер контекста (как рекомендуется для SQLAlchemy 2+), то это также может привести к выполнению фиксации. .create_all() не выполняет коммит, но это делает контекстный менеджер.

engine = create_engine("sqlite://", echo=True)
with engine.begin() as conn:  # "begin once" pattern
    Base.metadata.create_all(conn)
    """
CREATE TABLE thing (
    id INTEGER NOT NULL, 
    description VARCHAR NOT NULL, 
    PRIMARY KEY (id)
)


2024-06-12 06:46:09,601 INFO sqlalchemy.engine.Engine [no key 0.00027s] ()
2024-06-12 06:46:09,601 INFO sqlalchemy.engine.Engine COMMIT

Process finished with exit code 0
    """

Другие вопросы по теме