IT

SQL Chemy: 서로 다른 데이터베이스 간 결합 및 모듈 내 서로 다른 파일 사용

itgroup 2022. 11. 18. 21:34
반응형

SQL Chemy: 서로 다른 데이터베이스 간 결합 및 모듈 내 서로 다른 파일 사용

스택

사용하고 있는 것:

  • 파이썬 3.10.x
  • FastAPI 0.75.x
  • SQL Chemy 1.4.3배

요약

나는 단합된 단식을 만들고 있다.여러 레거시 데이터베이스에 대한 API 프로젝트(MariaDB 10.3에 저장된 백엔드 - 일부 레거시 소프트웨어의 경우 구조가 유지되어야 함).

SQLA 셋업에서는 데이터베이스 모듈을 사용하여 다음 작업을 수행합니다.

/disclosed.화이

import dotenv
import os

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

import .models as models

dotenv.load_dotenv()

engines = {
    'parts': create_engine("mysql+pymysql://" + os.environ['DB_URL'] + "/parts", pool_pre_ping=True, pool_recycle=300),
    'shop': create_engine("mysql+pymysql://" + os.environ['DB_URL'] + "/shop", pool_pre_ping=True, pool_recycle=300),
    'purchasing': create_engine("mysql+pymysql://" + os.environ['DB_URL'] + "/purchasing", pool_pre_ping=True, pool_recycle=300),
    "company": create_engine("mysql+pymysql://" + os.environ['DB_URL'] + "/company", pool_pre_ping=True, pool_recycle=300),
    "auth": create_engine("mysql+pymysql://" + os.environ['DB_URL'] + "/auth", pool_pre_ping=True, pool_recycle=300),
}

DBSession = sessionmaker(autocommit=False, autoflush=False, binds={
    # Catalogue
    models.Shop.Catalogue: engines["shop"],
    models.Shop.Sections: engines["shop"],
    models.Shop.Orders: engines["shop"],
    # ...
    # Parts
    models.Parts.Part: engines["parts"],
    models.Parts.BinLocations: engines["parts"],

    # ...
    #Purchasing
    models.Purchasing.SupplierOrder: engines["purchasing"],
    models.Purchasing.SupplierOrder: engines["purchasing"],
    # Company Data
    models.Company.Staffmember: engines["company"],
    models.Company.Suppliers: engines["company"],
    # API Auth
    models.Auth.User: engines["auth"],
    models.Auth.Privileges: engines["auth"],
})

# Dependency
def getDb():
    db = DBSession()
    try:
        yield db
    finally:
        db.close()

모든 모델에 대해 이것을 해야 하는 것은 조금 힘들지만 효과가 있습니다.

몇 개의 dbs를 가지고 있기 때문에, 저는 이 dbs를 작성하는 것이 논리적이라고 생각했습니다.models각 DB에 대한 하위 파일이 포함된 모듈. models.Parts,models.Shop,models.Purchase,models.Company,models.Auth기타.

/syslog/init.화이


from importlib.metadata import metadata
from sqlalchemy.orm import declarative_base

base = declarative_base()

from . import Auth, Parts, Shop, Catalogue, Purchasing, Shop

관계를 정상적으로 작성하려면 , 다음의 항목을 Import 합니다.Base의 오브젝트__init__.pymodels각 서브파일로 Import합니다.예를 들어 다음과 같습니다.

/http/Auth.py

from . import base as Base

from sqlalchemy.orm import relationship
from sqlalchemy import Column, Integer, String, Numeric, Date, DateTime, ForeignKey, null, or_, and_

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, nullable=False, primary_key=True)
    username = Column(String(256), nullable=False)
    passhash = Column(String(512), nullable=False)
    email = Column(String, nullable=False)
    enabled = Column(Integer, nullable=True)
    staffmember_id = Column(Integer, nullable=False)

    staffmember = relationship("Company.Staffmember", uselist=False)

/모델/회사화이

from . import base as Base

from sqlalchemy.orm import relationship
from sqlalchemy import Column, Integer, String, Numeric, Date, DateTime, ForeignKey, null, or_, and_

class Staffmebmer(Base):
    __tablename__ = 'staffmembers'

    id = Column(Integer, ForeignKey("users.staffmember_id"), nullable=False, primary_key=True)
    order = Column(Integer, default=0, nullable=False)
    name = Column(String, nullable=True)
    initial = Column(String, nullable=True)
    email = Column(String, nullable=False)
    enabled = Column(Integer, default=0, nullable=False)

    relationship("Auth.User", back_populates="staffmember")

다음 루트는 정상적으로 동작합니다.

demo.py


from fastapi import Depends

from sqlalchemy.orm import Session

from .. import app, databases, models

@app.get("/api/user/{id}")
async def read_items(id: int, db: Session=Depends(databases.getDb)):
    user = db.query(models.Auth.User).filter(
        models.Auth.User.id == id
    ).first()

    user.staffmember

    return user

URL에 접속하면 다음과 같이 반환됩니다. (네, 안전하지 않다는 것은 알고 있습니다.그것은 단지 관계가 기능하고 있다는 것을 보여주기 위한 것입니다!)

{
  "username": "mark",
  "passhash": "<my hash>",
  "enabled": 1,
  "email": "mark@demo.com",
  "id": 1,
  "staffmember_id": 5,
  "staffmember": {
    "order": 20,
    "name": "Mark",
    "email": "mark@demo.com",
    "kStaffmember": 5,
    "initial": "MB",
    "enabled": 1
  }
}

단, 가능한 사용자 이름으로 steffmember 이니셜을 사용하고 싶기 때문에 OAUTH Auth Authorize 스크립트에서 사용자를 쿼리할 때 다음 명령을 사용하려고 했습니다.


from ..models import Auth, Company

# 'username' is provided by the auth script from the standard username/password OAuth fields

def get_user(db: Session, username: str):
    db_user_data = db.query(Auth.User).join(Company.Staffmember).filter(
        or_(
            Auth.User.username == username,
            Auth.User.email == username,
            Company.Staffmember.initial == username
        )
    ).first()

예외로 지정됩니다.

(pymysql.err.ProgrammingError) (1146, "Table 'auth.staffmembers' doesn't exist")

제가 이 모든 것을 제대로 하고 있고, 이 문제를 해결할 수 있는 방법이 있을까요?

만약 누군가가 이 문제에 대해 주저하고 합리적인 답을 필요로 한다면, 나는 그것을 SQL Chemy Git 토론 페이지에 연결해서 내가 그것을 해결하는 데 도움이 되는 꽤 분별 있는 답변을 받았다.

https://github.com/sqlalchemy/sqlalchemy/discussions/8027

요약:

  • 서로 다른 데이터베이스에 대해 동일한 MySQL/mariadb 서버에 여러 엔진을 연결할 필요가 없습니다.데이터베이스 중 하나에서 세션을 시작하면 됩니다. Python SQA 코드의 모델 또는 모듈이 아니라 데이터베이스 이름입니다.

내 새 데이터베이스py:

import dotenv
import os

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

import .models as models

dotenv.load_dotenv()

DBengine = create_engine("mysql+pymysql://" + os.environ['DB_URL'] + "/parts", pool_pre_ping=True, pool_recycle=300)

DBSession = sessionmaker(bind=DBengine autocommit=False, autoflush=False)

# Dependency
def getDb():
    db = DBSession()
    try:
        yield db
    finally:
        db.close()
  • 위의 스타일을 사용하는 경우, 보다 명확하게 할 필요가 있습니다.ForeignKey()예를 들어 설명하겠습니다.ForeignKey("<db>.<table>.<field>")따라서 모든 데이터베이스와 테이블을 검색하도록 SQA에 명시적으로 지시합니다.

  • 데이터베이스 이름을 각 모델에 스키마로 지정해야 합니다(예: add).__table_args__ = { "schema": "<database name>" }- Python SQA 코드의 모델 또는 모듈이 아닌 데이터베이스 이름임을 기억하십시오.

새로운 /https/Auth.py

from . import base as Base

from sqlalchemy.orm import relationship
from sqlalchemy import Column, Integer, String, Numeric, Date, DateTime, ForeignKey, null, or_, and_

class User(Base):
    __tablename__ = 'users' #table is called 'users'
    __table_args__ = { "schema": "auth" } #database is called 'auth'

    id = Column(Integer, nullable=False, primary_key=True)
    username = Column(String(256), nullable=False)
    passhash = Column(String(512), nullable=False)
    email = Column(String, nullable=False)
    enabled = Column(Integer, nullable=True)
    staffmember_id = Column(Integer, nullable=False)

    staffmember = relationship("Company.Staffmember", uselist=False)

신규 / 모델 / 회사화이

from . import base as Base

from sqlalchemy.orm import relationship
from sqlalchemy import Column, Integer, String, Numeric, Date, DateTime, ForeignKey, null, or_, and_

class Staffmember(Base):
    __tablename__ = 'staffmembers' #table is called 'staffmembers'
    __table_args__ = { "schema": "company" } #database is called 'company'

    id = Column(Integer, ForeignKey("auth.users.staffmember_id"), nullable=False, primary_key=True)
    # ForeignKey now needs to know the database AND table name for the field it refers to
    order = Column(Integer, default=0, nullable=False)
    name = Column(String, nullable=True)
    initial = Column(String, nullable=True)
    email = Column(String, nullable=False)
    enabled = Column(Integer, default=0, nullable=False)

    relationship("Auth.User", back_populates="staffmember")

이 프로세스를 사용하면 SQA는 Join 및 관계에서 올바른 데이터베이스 이름 앞에 프레픽스를 붙이는 것을 인식하고 모든 것이 정상적으로 동작합니다.

언급URL : https://stackoverflow.com/questions/72229195/sqlalchemy-join-between-different-databases-and-using-different-files-in-a-modu

반응형