Как создать новую базу данных с помощью SQLAlchemy?


103

Используя SQLAlchemy, объект Engine создается следующим образом:

from sqlalchemy import create_engine
engine = create_engine("postgresql://localhost/mydb")

Доступ engineне удается, если база данных, указанная в аргументе create_engine(в данном случае mydb), не существует. Можно ли указать SQLAlchemy создать новую базу данных, если указанная база данных не существует?


2
Создать новую базу данных или просто таблицы? Я не встречал много ORM, которые действительно создают базы данных.
Нуфал Ибрагим


Ответы:


100

В postgres по умолчанию обычно присутствуют три базы данных. Если вы можете подключиться как суперпользователь (например, с postgresролью), вы можете подключиться к базам данных postgresили template1. По умолчанию pg_hba.conf разрешает postgresиспользовать postgresроль только указанному пользователю unix , поэтому проще всего просто стать этим пользователем. В любом случае создайте движок как обычно с пользователем, у которого есть разрешения на создание базы данных:

>>> engine = sqlalchemy.create_engine("postgres://postgres@/postgres")

Однако вы не можете использовать engine.execute(), потому что postgres не позволяет создавать базы данных внутри транзакций, а sqlalchemy всегда пытается запускать запросы в транзакции. Чтобы обойти это, получите базовое соединение от движка:

>>> conn = engine.connect()

Но соединение по-прежнему будет внутри транзакции, поэтому вы должны завершить открытую транзакцию с помощью commit:

>>> conn.execute("commit")

Затем вы можете приступить к созданию базы данных, используя для этого соответствующую команду PostgreSQL.

>>> conn.execute("create database test")
>>> conn.close()

3
У меня это сработало. В качестве примечания, когда я это сделал, у conn.execute('drop database DBWithCaps')меня были проблемы с тем, что он не распознал колпачки. conn.execute('drop database "DBWithCaps"')(с кавычками) работал нормально.
KobeJohn

Я знаю, что PostgreSQL ожидает, что все объекты будут в нижнем регистре, если они не указаны. Поэтому, если вы создали поле с помощью MyColumn, некоторые БД примут его как mycolumn. Другими словами, не знаю, как вы создали свою таблицу, но если она была создана с использованием кавычек, она будет чувствительна к регистру, поэтому, когда вы обращаетесь к ней в операторе SQL, вам также понадобятся кавычки.
гайарад

121

SQLAlchemy-Utils предоставляет настраиваемые типы данных и различные служебные функции для SQLAlchemy. Вы можете установить самую последнюю официальную версию с помощью pip:

pip install sqlalchemy-utils

В помощники базы данных включают в себя create_databaseфункции:

from sqlalchemy import create_engine
from sqlalchemy_utils import database_exists, create_database

engine = create_engine("postgres://localhost/mydb")
if not database_exists(engine.url):
    create_database(engine.url)

print(database_exists(engine.url))

2
Я получаю эту ошибку при попытке это точно кодоблока: psycopg2.OperationalError: fe_sendauth: no password supplied. При использовании "postgres://test:abc123@localhost:5432/test"получаюpsycopg2.OperationalError: FATAL: password authentication failed for user "test"
Гус

Извините за спам, но я попытался изменить порт на 9000, и теперь получаю следующее:"postgres://test:abc123@localhost:9000/test" psycopg2.OperationalError: server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request.
Guus

9

Это позволяет избежать ручного управления транзакциями при создании базы данных, предоставляя isolation_level='AUTOCOMMIT'для create_engineфункции:

import sqlalchemy

with sqlalchemy.create_engine(
    'postgresql:///postgres',
    isolation_level='AUTOCOMMIT'
).connect() as connection:
    connection.execute('CREATE DATABASE my_database')

Также, если вы не уверены, что база данных не существует, есть способ игнорировать ошибку создания базы данных из-за ее существования, подавив sqlalchemy.exc.ProgrammingErrorисключение:

import contextlib
import sqlalchemy.exc

with contextlib.suppress(sqlalchemy.exc.ProgrammingError):
    # creating database as above

Похоже, что вы не можете подключиться к серверу progres без указания базы данных, поэтому вы, вероятно, захотите подключиться к базе данных postgres по умолчанию для выполнения команд создания db, иначе он попытается подключиться к пользователю по умолчанию "база данных и пожаловаться, если она не существует.
Желудь,

0

Обратите внимание, что я не мог получить вышеуказанные предложения, database_existsпотому что всякий раз, когда я проверяю, существует ли база данных, используя if not, database_exists(engine.url):я получаю эту ошибку:

InterfaceError ('(pyodbc.InterfaceError) (\' 28000 \ ', u \' [28000] [Microsoft] [Собственный клиент SQL Server 11.0] [SQL Server]) Ошибка входа для пользователя \\ 'myUser \\'. (18456) (SQLDriverConnect); [28000] [Microsoft] [SQL Server Native Client 11.0] [SQL Server] Невозможно открыть базу данных «MY_DATABASE», запрошенную при входе в систему. Ошибка входа. (4060); [28000] [Microsoft] [SQL Server Native Клиент 11.0] [SQL Server] Ошибка входа для пользователя \\ 'myUser \\'. (18456); [28000] [Microsoft] [Собственный клиент SQL Server 11.0] [SQL Server] Невозможно открыть базу данных «MY_DATABASE», запрошенную пользователем . Не удалось войти в систему. (4060) \ ')',)

Также contextlib/suppressне работал, и я не использую, postgresпоэтому я сделал это, чтобы игнорировать исключение, если база данных уже существует с SQL Server:

import logging
import sqlalchemy

logging.basicConfig(filename='app.log', format='%(asctime)s-%(levelname)s-%(message)s', level=logging.DEBUG)
engine = create_engine('mssql+pyodbc://myUser:mypwd@localhost:1234/MY_DATABASE?driver=SQL+Server+Native+Client+11.0?trusted_connection=yes', isolation_level = "AUTOCOMMIT")

try: 
    engine.execute('CREATE DATABASE ' + a_database_name)
except Exception as db_exc:
    logging.exception("Exception creating database: " + str(db_exc))  
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.