SQLAlchemy, очистить содержимое базы данных, но не отбрасывать схему

Я разрабатываю приложение Pylons, основанное на существующей базе данных, поэтому я использую отражение. У меня есть файл SQL со схемой, которую я использовал для создания своей тестовой базы данных. Вот почему я не могу просто использовать drop_all и create_all.

Я хотел бы написать несколько модульных тестов и столкнулся с проблемой очистки содержимого базы данных после каждого теста. Я просто хочу стереть все данные, но оставить таблицы нетронутыми. Это возможно?

Приложение использует Postgres, и это то, что нужно также использовать для тестов.


person Juliusz Gonera    schedule 21.01.2011    source источник
comment
Вам следует использовать транзакции. docs.sqlalchemy. org / en / rel_0_7 / orm /   -  person charlax    schedule 12.09.2012


Ответы (3)


Я спросил о том же в группе Google SQLAlchemy и получил рецепт, который, похоже, работает хорошо (все мои таблицы опустошены). См. ветку для справки.

Мой код (отрывок) выглядит так:

import contextlib
from sqlalchemy import MetaData

meta = MetaData()

with contextlib.closing(engine.connect()) as con:
    trans = con.begin()
    for table in reversed(meta.sorted_tables):
        con.execute(table.delete())
    trans.commit()

Изменить: я изменил код, чтобы удалить таблицы в обратном порядке; якобы это должно гарантировать, что дети будут удалены раньше родителей.

person aknuds1    schedule 15.02.2011
comment
Вроде работает! Спасибо. Точный код, который я использую в Pylons: for table in reversed(meta.Base.metadata.sorted_tables): meta.Session.execute(table.delete()); meta.Session.commit() - person Juliusz Gonera; 25.02.2011
comment
Откуда engine? И contextlib? Это стандартная библиотека? Этот ответ был бы лучше, если бы импорт был полным. - person Zelphir Kaltstahl; 08.06.2017
comment
@MatthieuRodic, я подозреваю, что ваше редактирование здесь увеличивает риск того, что читатель будет сбит с толку этим ответом, а не уменьшит его. Для читателя, который ранее выполнял первоначальную настройку приложения SQLAlchemy, я думаю, что уже было неявно ясно, что такое meta, и поэтому редактирование не влияет на их понимание. Но для кого-то, кто никогда раньше не видел MetaData() и просто копается в приложении, созданном кем-то другим, я боюсь, что из-за ответа теперь может показаться, что он должен создать новый MetaData() экземпляр для использования с этот код, который, конечно, не сработает. - person Mark Amery; 23.11.2018
comment
Часть метаданных, указанная в ответе, не сработала. Во-первых, созданный мета-объект нигде не используется, и я понятия не имел, как заставить его работать. Однако в качестве обходного пути замена цикла for на for table in engine.table_names(): con.execute("DROP table " + table) - person Afsan Abdulali Gujarati; 04.12.2018
comment
Минусы в том, что этот подход не будет работать для таблиц с ограничением внешнего ключа. - person Roel; 15.06.2020

Для PostgreSQL с использованием TRUNCATE:

with contextlib.closing(engine.connect()) as con:
    trans = con.begin()
    con.execute('TRUNCATE {} RESTART IDENTITY;'.format(
        ','.join(table.name 
                 for table in reversed(Base.metadata.sorted_tables))))
    trans.commit()

Примечание: RESTART IDENTITY; гарантирует, что все последовательности также будут сброшены. Однако это медленнее, чем DELETE рецепт @ aknuds1 на 50%.

Другой рецепт - сначала удалить все таблицы, а затем воссоздать их. Это медленнее еще на 50%:

Base.metadata.drop_all(bind=engine)
Base.metadata.create_all(bind=engine)
person kolypto    schedule 09.08.2014
comment
metadata.Base.metadata.drop_all(bind=engine) выдает ошибку AttributeError: 'MetaData' object has no attribute 'Base' - person danio; 25.07.2016
comment
Но это может вызвать взаимоблокировки - person Roel; 15.06.2020
comment
@danio где ты нашел metadata.Base? наоборот: _2 _....: D - person kolypto; 25.07.2020
comment
@kolypto хороший вопрос, это было 4 года назад, так что я изо всех сил пытаюсь вспомнить! Но в приведенном выше комментарии к stackoverflow.com/a/5003705/12663 также используется meta.Base.metadata.sorted_tables, и ваш ответ не объясняет, где Base исходит из, так что я думаю, я пытался выяснить, откуда у вас Base, и использовал это! Может быть, API SQLAlchemy изменился между серией 0. и 1.? - person danio; 27.07.2020
comment
@danio Base - декларативная база для ваших моделей :) - person kolypto; 24.08.2020

Как насчет использования усечения:

TRUNCATE [ТАБЛИЦА] имя [, ...]

(http://www.postgresql.org/docs/8.4/static/sql-truncate.html)

Это удалит все записи в таблице, но оставит схему в действии.

person Joe L.    schedule 21.01.2011
comment
Это было бы нормально, но как я могу использовать это в SQLAlchemy? for table in meta.Base.metadata.tables.keys(): meta.Session.execute('truncate %s' % table); meta.Session.commit() вызывает такое сообщение об ошибке: InternalError: (InternalError) текущая транзакция прервана, команды игнорируются до конца блока транзакции 'truncate data.subkeywords' {} - person Juliusz Gonera; 22.01.2011
comment
Я исправил это, выполнив truncate %s cascade, но это мучительно медленно. Слишком медленно запускать его после каждого модульного теста ... - person Juliusz Gonera; 22.01.2011
comment
Вопрос задает sqlalchemy, а не sql. - person lnhubbell; 24.11.2015