SqlAlchemy 与 mm 的多对多关系 table

SqlAlchemy many to many relation with mm table

我还是Python的初学者,我被下面的关系卡住了。

三个tables:

sys_category class 看起来像这样:

class Category(Base):
    __tablename__ = "sys_category"

    uid = Column(
        Integer,
        ForeignKey("sys_category_record_mm.uid_local"),
        primary_key=True,
        autoincrement=True,
    )
    title = Column(String)
    products = relationship(
        "Product",
        uselist=False,
        secondary="sys_category_record_mm",
        back_populates="categories",
        foreign_keys=[uid],
    )

产品看起来像这样:

class Product(Base):
    __tablename__ = "tx_bdproductsdb_domain_model_product"

    uid = Column(
        Integer,
        ForeignKey(SysCategoryMMProduct.uid_foreign),
        primary_key=True,
        autoincrement=True,
    )
    
    category = Column(Integer)
    categories = relationship(
        Category,
        secondary=SysCategoryMMProduct,
        back_populates="products",
        foreign_keys=[uid],
    )

这里是mm table class 应该link 两者。

class SysCategoryMMProduct(Base):
    __tablename__ = "sys_category_record_mm"

    uid_local = Column(Integer, ForeignKey(Category.uid), primary_key=True)
    uid_foreign = Column(
        Integer, ForeignKey("tx_bdproductsdb_domain_model_product.uid")
    )
    fieldname = Column(String)

我目前被困住了,有人有什么想法吗?我在控制台中收到以下消息:

sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship Category.products - there are no foreign keys linking these tables via secondary table 'sys_category_record_mm'.  Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify 'primaryjoin' and 'secondaryjoin' expressions.
root@booba:/var/pythonWorks/crawler/develop/releases/current# python3 Scraper2.py 
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/sqlalchemy/orm/relationships.py", line 2739, in _determine_joins
    self.secondaryjoin = join_condition(
  File "<string>", line 2, in join_condition
  File "/usr/local/lib/python3.8/dist-packages/sqlalchemy/sql/selectable.py", line 1229, in _join_condition
    raise exc.NoForeignKeysError(
sqlalchemy.exc.NoForeignKeysError: Can't find any foreign key relationships between 'tx_bdproductsdb_domain_model_product' and 'sys_category_record_mm'.

sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship Category.products - there are no foreign keys linking these tables via secondary table 'sys_category_record_mm'.  Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify 'primaryjoin' and 'secondaryjoin' expressions.

谢谢:)

使用关联时 class 您应该直接引用关联。您需要这个而不是 secondary,因为您有与 link(即 fieldname)关联的数据。我更改了您的一些命名模式以使其更清晰。

有个不错的explanation of the association pattern in the sqlalchemy docs。该部分末尾有一个关于混合使用 secondary 和关联模式的大红色警告。

我使用 backref="related_categories" 在产品上自动创建 属性 related_categories。这是关联对象的列表,而不是实际类别。

from sqlalchemy import (
    create_engine,
    Integer,
    String,
    ForeignKey,

)
from sqlalchemy.schema import (
    Column,
)
from sqlalchemy.orm import declarative_base, relationship
from sqlalchemy.orm import Session


Base = declarative_base()

# This connection string is made up
engine = create_engine(
    'postgresql+psycopg2://user:pw@/db',
    echo=False)


class Category(Base):
    __tablename__ = "categories"

    uid = Column(
        Integer,
        primary_key=True,
        autoincrement=True,
    )
    title = Column(String)


class Product(Base):
    __tablename__ = "products"

    uid = Column(
        Integer,
        primary_key=True,
        autoincrement=True,
    )

    title = Column(String)


class SysCategoryMMProduct(Base):
    __tablename__ = "categories_products"
    uid = Column(Integer, primary_key=True)
    category_uid = Column(Integer, ForeignKey("categories.uid"))
    product_uid = Column(Integer, ForeignKey("products.uid"))
    fieldname = Column(String)

    product = relationship(
        "Product",
        backref="related_categories",
    )

    category = relationship(
        "Category",
        backref="related_products",
    )


Base.metadata.create_all(engine)

with Session(engine) as session:
    category = Category(title="kitchen")
    session.add(category)
    product = Product(title="spoon")
    session.add(product)
    association = SysCategoryMMProduct(
        product=product,
        category=category,
        fieldname="Extra metadata")
    session.add(association)
    session.commit()

    category = session.query(Category).first()
    assert len(category.related_products) == 1
    assert category.related_products[0].product.related_categories[0].category == category

    q = session.query(Category).join(Category.related_products).join(SysCategoryMMProduct.product).filter(Product.title == "spoon")
    print (q)
    assert q.first() == category

最后一个查询如下:

SELECT categories.uid AS categories_uid, categories.title AS categories_title 
FROM categories JOIN categories_products ON categories.uid = categories_products.category_uid JOIN products ON products.uid = categories_products.product_uid 
WHERE products.title = 'spoon'