Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

392
Visualizações
SQLAlchemy con enlaces múltiples: elija dinámicamente enlazar para consultar

Tengo 4 bases de datos diferentes, una para cada uno de mis clientes (clínicas médicas), las cuales tienen exactamente la misma estructura.

En mi aplicación, tengo modelos como Patient , Doctor , Appointment , etc.

Tomemos uno de ellos como ejemplo:

 class Patient(db.Model): __tablename__ = "patients" id = Column(Integer, primary_key=True) first_name = Column(String, index=True) last_name = Column(String, index=True) date_of_birth = Column(Date, index=True)

Descubrí que con la ayuda de enlaces puedo crear diferentes bases de datos y asociar cada modelo a un enlace diferente. Así que tengo esta configuración:

 app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://user:pass@localhost/main' app.config['SQLALCHEMY_BINDS'] = { 'clinic1':'mysql://user:pass@localhost/clinic1', 'clinic2':'mysql://user:pass@localhost/clinic2', 'clinic3':'mysql://user:pass@localhost/clinic3', 'clinic4':'mysql://user:pass@localhost/clinic4' }

Ahora estoy tratando de lograr dos cosas:

  1. Quiero que cuando cree tablas usando db.create_all() cree la tabla de patients en las 4 bases de datos (clinic1->clinic4)
  2. Quiero poder elegir un enlace específico de forma dinámica (en tiempo de ejecución), de modo que cualquier consulta como Patient.query.filter().count() se ejecute en la base de datos de enlace elegida

Idealmente, se comportaría así:

 with DbContext(bind='client1'): patients_count = Patient.query.filter().count() print(patients_count) # outside of the `with` context we are back to the default bind

Sin embargo, haciendo esto:

 patients_count = Patient.query.filter().count()

sin especificar un enlace, generará un error (ya que la tabla de patients no existe en el enlace predeterminado)

¡Cualquier ejemplo de código que pueda guiar cómo se puede hacer esto sería muy apreciado!

PD: Puede ser que sugiera no usar diferentes bases de datos y, en su lugar, usar una con diferentes columnas/tablas, pero siga mi ejemplo e intente explicar cómo se puede hacer esto usando este patrón de múltiples bases de datos idénticas.
¡Gracias!

over 4 years ago · Santiago Trujillo
1 Respostas
Responde à pergunta

0

1. Crear tablas en todos los enlaces

Observación: db.create_all() llama a self.get_tables_for_bind() .

Solución: anule SQLAlchemy get_tables_for_bind() para admitir '__all__' .

 class MySQLAlchemy(SQLAlchemy): def get_tables_for_bind(self, bind=None): result = [] for table in self.Model.metadata.tables.values(): # if table.info.get('bind_key') == bind: if table.info.get('bind_key') == bind or (bind is not None and table.info.get('bind_key') == '__all__'): result.append(table) return result

Uso:

 # db = SQLAlchemy(app) # Replace this db = MySQLAlchemy(app) # with this db.create_all()

2. Elija un enlace específico dinámicamente

Observación: SignallingSession get_bind() es responsable de determinar el enlace.

Solución:

  1. SignallingSession get_bind() para obtener la clave de vinculación de algún contexto.
  2. Anule SQLAlchemy create_session() para usar nuestra clase de sesión personalizada.
  3. Apoye el contexto para elegir un enlace específico en db para accesibilidad.
  4. Fuerce la especificación de un contexto para las tablas con '__all__' como clave de vinculación, anulando SQLAlchemy get_binds() para restaurar el motor predeterminado.
 class MySignallingSession(SignallingSession): def __init__(self, db, *args, **kwargs): super().__init__(db, *args, **kwargs) self.db = db def get_bind(self, mapper=None, clause=None): if mapper is not None: info = getattr(mapper.persist_selectable, 'info', {}) if info.get('bind_key') == '__all__': info['bind_key'] = self.db.context_bind_key try: return super().get_bind(mapper=mapper, clause=clause) finally: info['bind_key'] = '__all__' return super().get_bind(mapper=mapper, clause=clause) class MySQLAlchemy(SQLAlchemy): context_bind_key = None @contextmanager def context(self, bind=None): _context_bind_key = self.context_bind_key try: self.context_bind_key = bind yield finally: self.context_bind_key = _context_bind_key def create_session(self, options): return orm.sessionmaker(class_=MySignallingSession, db=self, **options) def get_binds(self, app=None): binds = super().get_binds(app=app) # Restore default engine for table.info.get('bind_key') == '__all__' app = self.get_app(app) engine = self.get_engine(app, None) tables = self.get_tables_for_bind('__all__') binds.update(dict((table, engine) for table in tables)) return binds def get_tables_for_bind(self, bind=None): result = [] for table in self.Model.metadata.tables.values(): if table.info.get('bind_key') == bind or (bind is not None and table.info.get('bind_key') == '__all__'): result.append(table) return result

Uso:

 class Patient(db.Model): __tablename__ = "patients" __bind_key__ = "__all__" # Add this

Caso de prueba:

 with db.context(bind='clinic1'): db.session.add(Patient()) db.session.flush() # Flush in 'clinic1' with db.context(bind='clinic2'): patients_count = Patient.query.filter().count() print(patients_count) # 0 in 'clinic2' patients_count = Patient.query.filter().count() print(patients_count) # 1 in 'clinic1'

Acerca de las claves externas que hacen referencia al enlace predeterminado

Tienes que especificar el schema .

Limitaciones:

  • mysql:
    • Los enlaces deben estar en la misma instancia de MySQL. De lo contrario, tiene que ser una columna simple.
    • El objeto externo en el enlace predeterminado ya debe estar comprometido.
      De lo contrario, al insertar un objeto que hace referencia a él, obtendrá este error de bloqueo:

      MySQLdb._exceptions.OperationalError: (1205, 'Se excedió el tiempo de espera de bloqueo; intente reiniciar la transacción')

  • SQLite: las claves externas en las bases de datos no se aplican.

Uso:

 # app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://user:pass@localhost/main' class PatientType(db.Model): __tablename__ = "patient_types" __table_args__ = {"schema": "main"} # Add this, based on database name id = Column(Integer, primary_key=True) # ... class Patient(db.Model): __tablename__ = "patients" __bind_key__ = "__all__" id = Column(Integer, primary_key=True) # ... # patient_type_id = Column(Integer, ForeignKey("patient_types.id")) # Replace this patient_type_id = Column(Integer, ForeignKey("main.patient_types.id")) # with this patient_type = relationship("PatientType")

Caso de prueba:

 patient_type = PatientType.query.first() if not patient_type: patient_type = PatientType() db.session.add(patient_type) db.session.commit() # Commit to reference from other binds with db.context(bind='clinic1'): db.session.add(Patient(patient_type=patient_type)) db.session.flush() # Flush in 'clinic1' with db.context(bind='clinic2'): patients_count = Patient.query.filter().count() print(patients_count) # 0 in 'clinic2' patients_count = Patient.query.filter().count() print(patients_count) # 1 in 'clinic1'
over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda