SQLAlchemy поддержка Postgres Schemas - PullRequest
       14

SQLAlchemy поддержка Postgres Schemas

44 голосов
/ 15 февраля 2012

Мы размещаем мультитенантное приложение с SQLAlchemy и postgres.Я смотрю на переход от наличия отдельных баз данных для каждого арендатора к одной базе данных с несколькими схемами.SQLAlchemy поддерживает это изначально?Я просто хочу, чтобы каждый запрос с префиксом предопределенной схемы получался ... например,

select * from client1.users

вместо

select * from users

Обратите внимание, что я хочу переключить схему длявсе таблицы в конкретном запросе / наборе запросов, а не просто одна таблица здесь и там.

Я полагаю, что это также может быть достигнуто с помощью пользовательского класса запросов, но я не могу представить, что что-то не получилось 'уже сделано в этом ключе.

Ответы [ 8 ]

47 голосов
/ 15 февраля 2012

Ну, есть несколько способов сделать это, и это зависит от структуры вашего приложения.Вот самый простой способ:

meta = MetaData(schema="client1")

Если ваше приложение работает по одному «клиенту» за раз во всем приложении, все готово.

Но что может бытьздесь неправильно то, что каждая таблица из этих метаданных находится в этой схеме.Если вы хотите, чтобы одно приложение поддерживало несколько клиентов одновременно (обычно это означает «мультитенант»), это было бы неудобно, так как вам нужно было бы создать копию метаданных и дублировать все сопоставления для каждого клиента.Этот подход может быть реализован, если вы действительно хотите, способ, которым он работает, заключается в том, что вы будете обращаться к каждому клиенту с помощью определенного сопоставленного класса, например:

client1_foo = Client1Foo()

, и в этом случае вы будете работать сРецепт «имя сущности» на http://www.sqlalchemy.org/trac/wiki/UsageRecipes/EntityName в сочетании с sometable.tometadata() (см. http://docs.sqlalchemy.org/en/latest/core/metadata.html#sqlalchemy.schema.Table.tometadata).

Итак, давайте скажем, что на самом деле работает несколько клиентов в приложении, но только по одному за разНа самом деле, самый простой способ сделать это в Postgresql - установить путь поиска, когда вы начнете работать с подключением:

# start request

# new session
sess = Session()

# set the search path
sess.execute("SET search_path TO client1")

# do stuff with session

# close it.  if you're using connection pooling, the
# search path is still set up there, so you might want to 
# revert it first
sess.close()

Последний подход - переопределить компилятор с помощью @компилирует расширение для вставки имени «схемы» в операторы. Это выполнимо, но было бы сложно, так как не везде есть согласованный хук, где генерируется «таблица». Лучше всего ставить путь поиска для каждого запроса.

20 голосов
/ 20 апреля 2018

Если вы хотите сделать это на уровне строки подключения, используйте следующее:

dbschema='schema1,schema2,public' # Searches left-to-right
engine = create_engine(
    'postgresql+psycopg2://dbuser@dbhost:5432/dbname',
    connect_args={'options': '-csearch_path={}'.format(dbschema)})

Но лучшим решением для мультиклиентского (мультитенантного) приложения является настройка другой базы данныхпользователь для каждого клиента и настройте соответствующий путь поиска для каждого пользователя:

alter role user1 set search_path = "$user", public
9 голосов
/ 20 февраля 2012

Вы можете управлять этим, используя интерфейс событий sqlalchemy.Поэтому, прежде чем создавать первое соединение, настройте прослушиватель в соответствии с

from sqlalchemy import event
from sqlalchemy.pool import Pool

def set_search_path( db_conn, conn_proxy ):
    print "Setting search path..."
    db_conn.cursor().execute('set search_path=client9, public')

event.listen(Pool,'connect', set_search_path )

Очевидно, это необходимо выполнить до создания первого соединения (например, при инициализации приложения)

Проблема, которую я вижу с решением session.execute (...), состоит в том, что это выполняется на определенном соединении, используемом сессией.Однако в sqlalchemy я не вижу ничего, что гарантировало бы, что сессия будет продолжать использовать одно и то же соединение бесконечно.Если он выбирает новое соединение из пула соединений, то он теряет настройку пути поиска.

Мне нужен такой подход, чтобы установить путь поиска приложения, который отличается от базы данных или пользователя.путь поиска.Я хотел бы иметь возможность установить это в конфигурации двигателя, но не могу найти способ сделать это.Использование события подключения работает.Я был бы заинтересован в более простом решении, если бы у кого-то было такое.

С другой стороны, если вы хотите обрабатывать несколько клиентов в приложении, тогда это не сработает - и я предполагаю, что сеанс будет.подход execute (...) может быть лучшим подходом.

5 голосов
/ 15 февраля 2012

В * 1001 имеется свойство схемы * Определения таблиц

Я не уверен, что это работает, но вы можете попробовать:

Table(CP.get('users', metadata, schema='client1',....)
0 голосов
/ 07 июня 2019

Теперь это можно сделать с помощью карты перевода схемы в Sqlalchemy 1.1.

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)

    __table_args__ = {'schema': 'per_user'}

При каждом запросе сеанс может быть настроен на обращение к другой схеме каждый раз:

session = Session()
session.connection(execution_options={
    "schema_translate_map": {"per_user": "account_one"}})

# will query from the ``account_one.user`` table

session.query(User).get(5)

Направлено это из SO ответа здесь .

Ссылка на документы Sqlalchemy .

0 голосов
/ 04 ноября 2018

Я пытался:

con.execute('SET search_path TO {schema}'.format(schema='myschema'))

, и это не сработало для меня.Затем я использовал параметр schema = в функции init:

# We then bind the connection to MetaData()
meta = sqlalchemy.MetaData(bind=con, reflect=True, schema='myschema')

Затем я определил таблицу с именем схемы

house_table = meta.tables['myschema.houses']

и все заработало.

0 голосов
/ 04 марта 2018

Я обнаружил, что ни один из приведенных выше ответов не работал с SqlAlchmeny 1.2.4.Это решение, которое сработало для меня.

from sqlalchemy import MetaData, Table
from sqlalchemy import create_engine    

def table_schemato_psql(schema_name, table_name):

        conn_str = 'postgresql://{username}:{password}@localhost:5432/{database}'.format(
            username='<username>',
            password='<password>',
            database='<database name>'
        )

        engine = create_engine(conn_str)

        with engine.connect() as conn:
            conn.execute('SET search_path TO {schema}'.format(schema=schema_name))

            meta = MetaData()

            table_data = Table(table_name, meta,
                              autoload=True,
                              autoload_with=conn,
                              postgresql_ignore_search_path=True)

            for column in table_data.columns:
                print column.name
0 голосов
/ 15 февраля 2012

Вы можете просто изменить свой путь поиска. Выпуск

set search_path=client9;

в начале сеанса, а затем просто оставляйте свои таблицы неквалифицированными.

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

http://www.postgresql.org/docs/current/static/ddl-schemas.html#DDL-SCHEMAS-PATH

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...