在 Pony ORM 中是否可以为多对多关系的中间 table 添加额外的属性?

Is it possible in Pony ORM to add extra attributes to the intermediate table of a many-to-many relationship?

在 Pony ORM 中,可以“自动”创建多对多关系。例如,from the documentation(对于0.6版本,强调我的):

In order to create many-to-many relationship you need to define both ends of the relationship as Set attributes:

class Product(db.Entity):
    tags = Set("Tag")

class Tag(db.Entity):
    products = Set(Product)

In order to implement this relationship in the database, Pony will create an intermediate table. This is a well known solution which allows you to have many-to-many relationships in relational databases.

是否可以在自动创建的中间体中创建一个额外的属性(列)table,这样不仅是“Product”和“Tag”的外键,还有时间戳?

如果是,怎么样?

如果没有,我想我必须明确地创建中间体 table。在那种情况下,我是否仍可以使用漂亮的 Set 属性定义(可能是中间 table,表示感兴趣的属性)?

目前需要定义明确的实体,像这样:

class Product(db.Entity):
    name = Required(str)
    tags = Set("ProductTag")

class Tag(db.Entity):
    name = Required(str, unique=True)
    products = Set("ProductTag")

class ProductTag(db.Entity):
    product = Required(Product)
    tag = Required(Tag)
    PrimaryKey(product, tag)
    timestamp = Required(datetime, default=datetime.now)

Pony 在 Django 中不支持像 through 这样的虚拟 Set 属性,但我们计划在将来添加它们。现在您需要明确地使用中间 table。

为产品添加标签

p1 = Product[1]
tag1 = Tag.get(name='smartphones')

p1.tags.create(tag=tag1)
# or:
ProductTag(product=p1, tag=tag1)

正在从产品中删除标签:

ProductTag[p1, tag1].delete()

检查产品是否有特定标签:

ProductTag.get(product=p1, tag=tag1) is not None

另外,Pony支持attribute lifting的概念。这意味着在 Pony 中,任何集合属性都具有其项的所有属性。此类集合属性的值是单个项目的所有值的集合。例如,为了获取特定产品的所有标签,您可以这样写:

p1.tags.tag

p1.tags 表达式 returns ProductTag 项的集合。每个 ProductTag 对象都有 tag 属性 指向特定的标签对象。所以 p1.tags.tag returns 与特定 Product 对象链接的所有 Tag 对象的集合。

可以在查询中使用属性提升。例如,要找到带有标签 smartphones 的所有产品,您可以编写以下查询:

select(p for p in Product if 'smartphones' in p.tags.tag.name)

这里,p.tagsProductTag个对象的集合,p.tags.tagTag个对象的集合,p.tags.tag.name是标签的集合名字。上面的查询是以下查询的语法糖:

select(p for p in Product if 'smartphones' in select(item.tag.name for item in p.tags))

此外,查询可以重写为:

select(pt.product for pt in ProductTag if pt.tag.name == 'smartphones')