如何在SQLAlchemy中删除表格?

90

我想使用SQLAlchemy删除一个表。

由于我一遍又一遍地测试,我想删除名为my_users的表,以便每次都可以从头开始。

到目前为止,我正在使用SQLAlchemy通过engine.execute()方法执行原始SQL语句:

sql = text('DROP TABLE IF EXISTS my_users;')
result = engine.execute(sql)

然而,我想知道是否有一种标准方法来这样做。我能找到的仅有的一种是drop_all(),但它会删除所有结构,而不仅仅是一个特定的表:

Base.metadata.drop_all(engine)   # all tables are deleted
例如,考虑这个非常基本的示例。它包含一个SQLite架构,其中有一个名为my_users的单个表,我向其中添加了一些内容。
from sqlalchemy import create_engine, Column, Integer, String, text
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine('sqlite://', echo=False)
Base = declarative_base()

class User(Base):
    __tablename__ = "my_users"

    id = Column(Integer, primary_key=True)
    name = Column(String)

    def __init__(self, name):
        self.name = name

# Create all the tables in the database which are
# defined by Base's subclasses such as User
Base.metadata.create_all(engine)

# Construct a sessionmaker factory object
session = sessionmaker()

# Bind the sessionmaker to engine
session.configure(bind=engine)

# Generate a session to work with
s = session()

# Add some content
s.add(User('myname'))
s.commit()

# Fetch the data
print(s.query(User).filter(User.name == 'myname').one().name)

对于这种特定情况,drop_all()会起作用,但是从我开始拥有多个表并且我想保留其他表时,它将不方便。


注意:必须在 text(...) 中包含 SQL 查询语句才能使其工作。如果省略此步骤,则 SQLAlchemy 将不起作用,也不会提供任何反馈来解释为什么没有工作。 - Contango
5个回答

95

只需对表对象调用drop()方法。 从文档中:

使用给定的 Connectable 连接性为此表发出 DROP 语句。

在您的情况下,应该是这样的:

User.__table__.drop()
如果您遇到以下异常:
sqlalchemy.exc.UnboundExecutionError: Table object 'my_users' is not bound to an Engine or Connection. Execution can not proceed without a database to execute against
您需要通过引擎:
User.__table__.drop(engine)

2
只有在表存在的情况下才能正常工作,如果您删除整个.db文件并想要重新创建它,则会出现错误。我对此进行了后续问题,请查看:https://dev59.com/FZrga4cB1Zd3GeqPrcHU - NumesSanguis
这不能在事务中使用。如果表被删除,我想回滚事务,但是其他查询(如重新创建表)引发异常。使用这个方法我做不到。 - Karina Klinkevičiūtė
1
@KarinaKlinkevičiūtė 你可以先检查表格是否存在;例如:engine = db.engine; if not engine.dialect.has_table(engine, ModelClass.__tablename__): ModelClass.__table__.create(engine) - code_dredd
1
使用 db = SQLAlchemy(app),然后执行 User.__table__.drop(db.engine) 对我有用。 - Sameer Kumar
如果您发现调用 User.__table__.drop() 似乎挂起/冻结(特别是在Postgres中),请参见此处的解决方案:https://github.com/sqlalchemy/sqlalchemy/issues/4685 - Brendano257
显示剩余3条评论

37

除了调用 cls.__table__.drop(your_engine),你也可以尝试这样做:

Base.metadata.drop_all(bind=your_engine, tables=[User.__table__])

这种方法和create_all()方法一样,接受一个可选参数tables,它需要sqlalchemy.sql.schema.Table实例的迭代器。

您可以通过这种方式控制要创建或删除哪些表。


3
对我来说,这个回答在语义上更清晰。创建或删除表更与数据库层面相关,因此最好使用Base而不是映射类进行处理。也许SqlAlchemy在后台执行的是相同的操作。 - WeiHao

21

如果您无法访问表格类,但需要按表格名称删除表格,则使用以下代码:

import logging
from sqlalchemy import MetaData
from sqlalchemy import create_engine
from sqlalchemy.engine.url import URL
from sqlalchemy.ext.declarative import declarative_base

DATABASE = {
   'drivername': 'sqlite',
   # 'host': 'localhost',
   # 'port': '5432',
   # 'username': 'YOUR_USERNAME',
   # 'password': 'YOUR_PASSWORD',
   'database': '/path/to/your_db.sqlite'
}

def drop_table(table_name):
   engine = create_engine(URL(**DATABASE))
   base = declarative_base()
   metadata = MetaData(engine, reflect=True)
   table = metadata.tables.get(table_name)
   if table is not None:
       logging.info(f'Deleting {table_name} table')
       base.metadata.drop_all(engine, [table], checkfirst=True)

drop_table('users')

3
请注意,已发布了一个新答案(https://dev59.com/vlsV5IYBdhLWcg3wrQgp#66376565),提到了`MetaData(engine,reflect = True)`的弃用。 - fedorqui

14

如何按名称删除表格

这是对@Levon答案的更新,因为MetaData(engine, reflect=True)现已被弃用。如果您无法访问表类或想要按其表名删除表格,则此方法很有用。

from sqlalchemy import MetaData
from sqlalchemy import create_engine
from sqlalchemy.engine.url import URL
from sqlalchemy.ext.declarative import declarative_base

DATABASE = {
   'drivername': 'sqlite',
   # 'host': 'localhost',
   # 'port': '5432',
   # 'username': 'YOUR_USERNAME',
   # 'password': 'YOUR_PASSWORD',
   'database': '/path/to/your_db.sqlite'
}

engine = create_engine(URL(**DATABASE))

def drop_table(table_name, engine=engine):
    Base = declarative_base()
    metadata = MetaData()
    metadata.reflect(bind=engine)
    table = metadata.tables[table_name]
    if table is not None:
        Base.metadata.drop_all(engine, [table], checkfirst=True)

drop_table('users')

如何使用表类删除表(首选)

否则,您可以选择使用cls.__table__.drop(engine)cls.__table__.create(engine),例如:

User.__table__.drop(engine)
User.__table__.create(engine)

9
以下是您可以在iPython中执行的示例代码,用于测试在Postgres上创建和删除表格。
from sqlalchemy import * # imports all needed modules from sqlalchemy

engine = create_engine('postgresql://python:python@127.0.0.1/production') # connection properties stored

metadata = MetaData() # stores the 'production' database's metadata

users = Table('users', metadata,
Column('user_id', Integer),
Column('first_name', String(150)),
Column('last_name', String(150)),
Column('email', String(255)),
schema='python') # defines the 'users' table structure in the 'python' schema of our connection to the 'production' db

users.create(engine) # creates the users table

users.drop(engine) # drops the users table

您可以使用相同的示例和截图在Wordpress上预览我的文章:oscarvalles.wordpress.com(搜索SQL Alchemy)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接