如何使用基于 SQLAlchemy 中角色的判别器正确建模连接表继承

发布于 2024-11-10 10:37:33 字数 398 浏览 2 评论 0原文

是否可以指定另一个表中的鉴别器列?我如何使用声明式来做到这一点?

原因是我有一个连接表继承,

class User(Base):
    id = Column(...)

class Customer(User):
    customer_id = Column('id', ...)

class Mechanic(User):
    mechanic_id = Column('id', ...)

class Role(Base):
    id = Column(..)

但想通过用户拥有的角色来区分。用户可以具有买方角色或卖方角色或两者兼而有之。

这是模拟这种情况的正确方法吗?

请注意,还有买方特定数据和卖方特定数据,这就是我使用连接表继承的原因。

Is it possible to specify a discriminator column from another table? How do I do this with Declarative?

The reason for this is I have a joined table inheritance with

class User(Base):
    id = Column(...)

class Customer(User):
    customer_id = Column('id', ...)

class Mechanic(User):
    mechanic_id = Column('id', ...)

class Role(Base):
    id = Column(..)

but want to discriminate by Roles a user has. A user could have a buyer role or a seller role or both.

Is this the correct way to model this scenario?

As a note, there is Buyer specific data and Seller specific data as well, and that is why I am using the joined table inheritance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

魂归处 2024-11-17 10:37:33

这是一种实现特定结果的笨拙且低效的方法,如果目标用户上存在多个角色,该方法也会失败:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

role_to_user = Table('role_to_user', Base.metadata,
    Column('role_id', Integer, ForeignKey('role.id'), primary_key=True),
    Column('user_id', Integer, ForeignKey('user.id'), primary_key=True),
)

class Role(Base):
    __tablename__ = 'role'
    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)

    discrim = select([Role.name])
    discrim_col = discrim.label("foo")
    type = column_property(discrim_col)
    roles = relationship("Role", secondary=role_to_user)
    __mapper_args__ = {'polymorphic_on': discrim_col}

User.discrim.append_whereclause(Role.id==role_to_user.c.role_id)
User.discrim.append_whereclause(role_to_user.c.user_id==User.id)

class Customer(User):
    __tablename__ = 'customer'
    id = Column(Integer, ForeignKey(User.id), primary_key=True)
    __mapper_args__ = {'polymorphic_identity':'customer'}

class Mechanic(User):
    __tablename__ = 'mechanic'
    id = Column(Integer, ForeignKey(User.id), primary_key=True)

    __mapper_args__ = {'polymorphic_identity':'mechanic'}

e = create_engine('sqlite://', echo=True)

Base.metadata.create_all(e)

s = Session(e)

m, c = Role(name='mechanic'), Role(name='customer')
s.add_all([m, c])
s.add_all([Mechanic(roles=[m]), Customer(roles=[c])])
s.commit()

print Session(e).query(User).all()

接下来,如果我遇到基于零分配行为的问题,那么我实际上会这样做或更多角色 - 我会将行为放在角色中,而不是角色的所有者中:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    roles = relationship("Role", secondary= Table(
                    'role_to_user', Base.metadata,
                    Column('role_id', Integer, 
                            ForeignKey('role.id'), 
                            primary_key=True),
                    Column('user_id', Integer, 
                            ForeignKey('user.id'), 
                            primary_key=True),
                    ),
                    lazy="subquery"
                )

    def operate_on_roles(self):
        for role in self.roles:
            role.operate(self)

class Role(Base):
    __tablename__ = 'role'
    id = Column(Integer, primary_key=True)
    type = Column(String, nullable=False)
    __mapper_args__ = {'polymorphic_on': type}

class CustomerRole(Role):
    __tablename__ = 'customer'
    id = Column(Integer, ForeignKey(Role.id), primary_key=True)
    __mapper_args__ = {'polymorphic_identity':'customer'}

    def operate(self, user):
        print "user %s getting my car fixed!" % user.name

class MechanicRole(Role):
    __tablename__ = 'mechanic'
    id = Column(Integer, ForeignKey(Role.id), primary_key=True)
    __mapper_args__ = {'polymorphic_identity':'mechanic'}

    def operate(self, user):
        print "user %s fixing cars!" % user.name

e = create_engine('sqlite://', echo=True)

Base.metadata.create_all(e)

s = Session(e)

m, c = MechanicRole(), CustomerRole()
s.add_all([m, c])
s.add_all([
        User(name='u1', roles=[m, c]),
        User(name='u2', roles=[c]),
        User(name='u3', roles=[m]),
    ])
s.commit()

for user in s.query(User):
    user.operate_on_roles()

第二个示例产生输出:

user u1 fixing cars!
user u1 getting my car fixed!
user u2 getting my car fixed!
user u3 fixing cars!

如您所见,第二个示例清晰且高效,而第一个示例只是一个思想实验。

Here's an awkward and inefficient way to achieve the specific result, which will also fail if more than one Role is present on the target User:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

role_to_user = Table('role_to_user', Base.metadata,
    Column('role_id', Integer, ForeignKey('role.id'), primary_key=True),
    Column('user_id', Integer, ForeignKey('user.id'), primary_key=True),
)

class Role(Base):
    __tablename__ = 'role'
    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)

    discrim = select([Role.name])
    discrim_col = discrim.label("foo")
    type = column_property(discrim_col)
    roles = relationship("Role", secondary=role_to_user)
    __mapper_args__ = {'polymorphic_on': discrim_col}

User.discrim.append_whereclause(Role.id==role_to_user.c.role_id)
User.discrim.append_whereclause(role_to_user.c.user_id==User.id)

class Customer(User):
    __tablename__ = 'customer'
    id = Column(Integer, ForeignKey(User.id), primary_key=True)
    __mapper_args__ = {'polymorphic_identity':'customer'}

class Mechanic(User):
    __tablename__ = 'mechanic'
    id = Column(Integer, ForeignKey(User.id), primary_key=True)

    __mapper_args__ = {'polymorphic_identity':'mechanic'}

e = create_engine('sqlite://', echo=True)

Base.metadata.create_all(e)

s = Session(e)

m, c = Role(name='mechanic'), Role(name='customer')
s.add_all([m, c])
s.add_all([Mechanic(roles=[m]), Customer(roles=[c])])
s.commit()

print Session(e).query(User).all()

Next, here is how I would actually do it, if I had the issue of assigning behaviors based on zero or more Roles - I'd put the behavior in the Role, not the owner of the Role:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    roles = relationship("Role", secondary= Table(
                    'role_to_user', Base.metadata,
                    Column('role_id', Integer, 
                            ForeignKey('role.id'), 
                            primary_key=True),
                    Column('user_id', Integer, 
                            ForeignKey('user.id'), 
                            primary_key=True),
                    ),
                    lazy="subquery"
                )

    def operate_on_roles(self):
        for role in self.roles:
            role.operate(self)

class Role(Base):
    __tablename__ = 'role'
    id = Column(Integer, primary_key=True)
    type = Column(String, nullable=False)
    __mapper_args__ = {'polymorphic_on': type}

class CustomerRole(Role):
    __tablename__ = 'customer'
    id = Column(Integer, ForeignKey(Role.id), primary_key=True)
    __mapper_args__ = {'polymorphic_identity':'customer'}

    def operate(self, user):
        print "user %s getting my car fixed!" % user.name

class MechanicRole(Role):
    __tablename__ = 'mechanic'
    id = Column(Integer, ForeignKey(Role.id), primary_key=True)
    __mapper_args__ = {'polymorphic_identity':'mechanic'}

    def operate(self, user):
        print "user %s fixing cars!" % user.name

e = create_engine('sqlite://', echo=True)

Base.metadata.create_all(e)

s = Session(e)

m, c = MechanicRole(), CustomerRole()
s.add_all([m, c])
s.add_all([
        User(name='u1', roles=[m, c]),
        User(name='u2', roles=[c]),
        User(name='u3', roles=[m]),
    ])
s.commit()

for user in s.query(User):
    user.operate_on_roles()

The second example produces the output:

user u1 fixing cars!
user u1 getting my car fixed!
user u2 getting my car fixed!
user u3 fixing cars!

As you can see the second example is clear and efficient while the first is just a thought experiment.

撧情箌佬 2024-11-17 10:37:33

在最一般的情况下,我会说“不”。

在最一般的情况下,“买家”和“卖家”这两个词并不描述角色。它们描述了两方之间的关系。 (两个人之间、两个公司之间或个人与公司之间。)这种关系取决于一方至少从另一方购买一件东西。

您可能需要买家和卖家表(带有用户表的外键)来记录仅与买家或卖家相关的详细信息。但在没有任何此类详细信息的情况下,销售表(例如 {buyer_id, seller_id, Product, date_time})将是记录谁从谁那里购买了什么商品的“正常”方式。

In the most general case, I'd say "No".

In the most general case, the words buyer and seller don't describe roles. They describe a relationship between two parties. (Between two individuals, between two companies, or between an individual and a company.) The relationship depends on one party buying at least one thing from the other party.

You might need tables of buyers and sellers (with foreign keys to the users table) to record details related only to the buyer or only to the seller. But in the absence of any such details, a table of sales, like {buyer_id, seller_id, product, date_time}, would be the "normal" way to record who bought what from whom.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文