PonyORM / Postgres мультитенантные опции - PullRequest
1 голос
/ 05 октября 2019

У меня есть приложение, запущенное в производство, которое я создал для одного клиента, которое я хочу преобразовать для поддержки нескольких «арендаторов».

В настоящее время я использую базу данных Postgres, где все мои данные хранятся в одной базе данных в схеме public по умолчанию. Я хотел бы изолировать каждого арендатора в отдельной схеме Postgres. В идеале пользовательский интерфейс моего приложения должен вызывать мой API с использованием субдомена арендатора. В before_request я бы каким-то образом смог установить все запросы к базе данных во время текущего контекста запроса только для запроса схемы этого арендатора, возможно ли это?

Я предполагаю, что идеальное решение будет чем-то похожим на этот надуманный пример:

from flask import Flask, request, jsonify
from pony.orm import Database, Required

app = Flask(__name__)
db = Database(**{<db_connection_dict>})

class User(db.Entity):
  email = Required(str)
  password = Required(str)

  @classmethod
  def login(cls, email: str, password: str) -> str:
    user = cls.get(lambda u: u.email.lower() == email.lower())
    if not user:
      return None
    password_is_valid = <method_to_check_hashed_pasword>
    if not password_is_valid:
      return None
    return <method_to_generate_jwt>

db.generate_mapping()

@app.before_request
def set_tenant():
  tenant_subdomain = request.host.split(".")[0]
  // MISSING STEP.. set_schema is a fictitous method, does something similar to this exist?
  db.set_schema(schema=tenant_subdomain)??

@app.route("auth/login", methods=["POST"]
def login_route():
  data = request.get_json()
  jwt = User.login(data["email"], data["password"])
  if not jwt:
   return make_response({}, 403)
  return make_response(jsonify(data=jwt), 200)

Я наткнулся на интересный / простой пример с использованием SQLAlchemy. Если это невозможно с PonyORM, я мог бы рассмотреть возможность переноса своих моделей на SQLAlchemy, но упустил бы простоту Pony: (

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

@db.on_connect()
def set_request_context_tenant_schema(db, connection) -> None:
    subdomain = request.host.split(".")[0]
    cursor = connection.cursor()
    cursor.execute(f"SET search_path TO {subdomain}, public;")
...