Подтвердить что ты не робот

Поддержка SQLAlchemy схем Postgres

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

select * from client1.users

вместо

select * from users

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

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

4b9b3361

Ответ 1

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

meta = MetaData(schema="client1")

Если способ запуска вашего приложения - это один "клиент" одновременно в рамках всего приложения, все готово.

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

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()

Окончательный подход заключается в том, чтобы переопределить компилятор с помощью расширения @compiles, чтобы вставлять имя "schema" внутри операторов. Это выполнимо, но было бы сложно, потому что везде не было согласованного крючка "Таблица". Ваш лучший выбор, вероятно, задает путь поиска по каждому запросу.

Ответ 2

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

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

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

alter role user1 set search_path = "$user", public

Ответ 3

Вы можете управлять этим, используя интерфейс событий 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, который гарантирует, что сеанс будет продолжать использовать одно и то же соединение неограниченно. Если он забирает новое соединение из пула соединений, он потеряет настройку пути поиска.

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

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

Ответ 4

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

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

Table(CP.get('users', metadata, schema='client1',....)

Ответ 5

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

set search_path=client9;

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

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

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

Ответ 6

Я не нашел, что ни один из вышеперечисленных ответов не работал с 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

Ответ 7

Я старался:

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']

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

Ответ 8

Теперь это можно сделать с помощью карты перевода схемы в 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)

Направил это из ТАКОГО ответа здесь.

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