簡體   English   中英

SQLAlchemy 命令式映射無法使用外鍵為另一個實體添加實體

[英]SQLAlchemy imperative mapping can't add entity using foreign key for another entity

在嘗試使用外鍵添加新實體時,我遇到了 SQLAlchemy 命令式映射的一些問題。

如果我將映射的 class 定義為:

@attr.s
class Foreign:
    id = attr.ib(type=int, validator=optional(instance_of(int)), default=None)
    name = attr.ib(type=str, validator=instance_of(str), default=None)


@attr.s
class Primary:
    id = attr.ib(type=int, validator=optional(instance_of(int)), default=None)
    foreign_id = attr.ib(type=int, validator=optional(instance_of(int)), default=None)
    foreign = attr.ib(type=Foreign, validator=optional(instance_of(Foreign)), default=None)

添加新實體時,例如:

session.add(Primary(foreign_id=foreign_id))
session.commit()

它失敗了,因為它用 None 替換了 foreign_id,因為我猜它期望填充外部屬性。

這些是我的表:

mapper_registry = registry()

primary = Table(
    "primary",
    metadata,
    Column("id", Integer, primary_key=True, autoincrement=True),
    Column("foreign_id", Integer, ForeignKey("foreign.id"), nullable=False),
)

foreign = Table(
    "foreign",
    metadata,
    Column("id", Integer, primary_key=True, autoincrement=True),
    Column("name", String(20), nullable=False),
)

def configure_mappers():
    mapper_registry.map_imperatively(Foreign, foreign)

def configure_mappers():
    mapper_registry.map_imperatively(
        Primary,
        primary,
        properties={
            "foreign": relationship(Foreign),
        },
    )

但是,如果我從 Mapped Class 中刪除foreign屬性,它會完美運行。

如果我映射的 class 具有到外鍵和外鍵 class 的映射,如何使用外鍵添加新實體?

來自attrs 文檔

嘗試以簡潔易用的方式設計你的類——而不是基於你的數據庫格式。 數據庫格式可以隨時更改,您會遇到難以更改的糟糕 class 設計。 將函數和類方法作為現實與最適合您使用的方法之間的過濾器。

如果您絕對需要從 Python 代碼訪問 id 和/或從外部 id 和對象實例化您的 Primary 對象,您可以檢查 class 實例方法並編寫兩個 ZA2F2ED4F8EBC2CBB1DZ0 方法到 instanciate Primary:

  • 一個用外部 object 實例實例化 Primary,
  • 另一個使用 Foreign object id 作為外鍵來實例化 Primary。

替代設計

據我了解,ORM(尤其是其命令式映射風格)的價值在於能夠表達您的代碼,而無需處理諸如 id 和外鍵之類的數據庫概念。

此外,能夠實例化具有離散 id 的 Primary 或 Foreign 對象與將其用作主鍵這一事實相矛盾:在 Python 代碼中沒有強制執行具有唯一鍵的約束。

命令式映射的優勢在於將域 object 與其數據庫實現完全解耦,因此您可以讓 SQLAlchemy 處理幕后的 id。

model.py

import attr
from attr.validators import instance_of
from attr.validators import optional


@attr.s
class Foreign:
    name = attr.ib(type=str, validator=instance_of(str), default=None)


@attr.s
class Primary:
    name = attr.ib(type=str, validator=instance_of(str), default=None)
    foreign = attr.ib(type=Foreign, validator=optional(instance_of(Foreign)), default=None)

orm.py

import model
from sqlalchemy.orm import registry
from sqlalchemy.orm import relationship
from sqlalchemy.schema import MetaData
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import ForeignKey
from sqlalchemy import String
from sqlalchemy import Table


metadata = MetaData()
mapper_registry = registry()

table_primary = Table(
    "primary",
    metadata,
    Column("id", Integer, primary_key=True, autoincrement=True),
    Column("foreign_id", Integer, ForeignKey("foreign.id"), nullable=False),
)


table_foreign = Table(
    "foreign",
    metadata,
    Column("id", Integer, primary_key=True, autoincrement=True),
    Column("name", String(20), nullable=False),
)


def configure_mappers():
    mapper_registry.map_imperatively(model.Foreign, table_foreign)

    mapper_registry.map_imperatively(
        model.Primary,
        table_primary,
        properties={
            "foreign": relationship(model.Foreign),
        },
    )

用法

import orm
import model
import sqlalchemy

engine = sqlalchemy.create_engine("sqlite:///my_db.db")

orm.metadata.create_all(engine)
orm.configure_mappers()
Session = sqlalchemy.orm.sessionmaker(bind=engine)
session = Session()


foreign_1 = model.Foreign(name="first foreign")
foreign_2 = model.Foreign(name="second foreign")
foreign_3 = model.Foreign(name="third foreign")
primary_a = model.Primary(name="first primary", foreign=foreign_3)
primary_b = model.Primary(name="second primary", foreign=foreign_2)
session.add_all([foreign_1, primary_a, primary_b])
session.commit()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM