【问题标题】:SQLAlchemy generic relationship mixinSQLAlchemy 通用关系混合
【发布时间】:2016-08-17 08:51:01
【问题描述】:

我正在处理 SQLAlchemy 定义一堆 mixin classes 应用程序应该能够导入和扩展它们的模型。

查看documentation 时,mixin 类是在知道最终表名的情况下创建的,但是对于通用库,应用程序将使用的最终表名是未知的。

参加以下混合类:

import sqlalchemy as sa

class UserMixin(object):
    id = sa.Column(sa.Integer(), primary_key=True)
    first_name = sa.Column(sa.Unicode(255))
    last_name  = sa.Column(sa.Unicode(255))

class ItemMixin(object):
    id = sa.Column(sa.Integer(), primary_key=True)
    name = sa.Column(sa.Unicode(255))
    short_description = sa.Column(sa.Unicode(255))

class OrdersMixin(object):
    id = sa.Column(sa.Integer(), primary_key=True)
    user_id = sa.Column(sa.Integer(), sa.ForeignKey('???'))
    item_id = sa.Column(sa.Integer(), sa.ForeignKey('???'))

然后是定义其模型的应用程序:

import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class MyUser(UserMixin, Base):
    __tablename__ = 'myuser'

class MyItem(ItemMixin, Base):
    __tablename__ = 'myitem'
    total = sa.Column(sa.Integer())

class MyOrders(OrdersMixin, Base):
    __tablename__ = 'myorders'

我对这个模型有两个问题:

  1. 除了在扩展模型中重新定义关系列之外,mixin 类如何自行构建关系。
  2. 外键的类型由 mixin 类假定,但表的 id 可能来自应用程序本身或来自另一个 mixin 类。

我尝试实施的模型是否正确?解决这个问题的正确方法是什么?

【问题讨论】:

  • 你好,你能解决这个问题吗?
  • @alejom99 我只是放弃了这个想法并重组了代码。

标签: python orm sqlalchemy


【解决方案1】:

Mixin 是一个将其数据复制到表中的类,但对于 SQL 而言,重要的是您是该数据(表)的所有者还是引用(外键)。

您似乎正在尝试创建既是事实来源又是参考来源的 Mixin。这在 SQL 中是不可能的。

让你的例子更进一步,像这样定义OrdersMixin会让我认为问题更加明显。

class OrdersMixin(UserMixin, ItemMixin):
    id = sa.Column(sa.Integer(), primary_key=True)

例如,一旦事情解决,MyOrders 就会像这样结束。

class MyOrders(Base):
    __tablename__ = 'myorders'
    # This is from UserMixin
    id = sa.Column(sa.Integer(), primary_key=True)
    first_name = sa.Column(sa.Unicode(255))
    last_name  = sa.Column(sa.Unicode(255))

    # This is from ItemMixin
    id = sa.Column(sa.Integer(), primary_key=True)
    name = sa.Column(sa.Unicode(255))
    short_description = sa.Column(sa.Unicode(255))

    # From OrdersMixin
    id = sa.Column(sa.Integer(), primary_key=True)  # This is defined last so it overrides all others with the same name.
    user_id = sa.Column(sa.Integer(), sa.ForeignKey('???'))
    item_id = sa.Column(sa.Integer(), sa.ForeignKey('???'))

随着您如何使用 Mixin 定义使用该 Mixin 的任何表,primary_keys 的 id 列会发生冲突。此外,您正在复制 Mixin 中的每一列,通常您希望在 SQL 中避免这种情况(请参阅Database normal form)。

最终的结果是这样的。这是一大堆列,这意味着您不需要引用任何其他表,并且您拥有的所有引用 id 都被覆盖了,这意味着您无论如何都无法加入它们。

class MyOrders(Base):
    __tablename__ = 'myorders'
    first_name = sa.Column(sa.Unicode(255))
    last_name  = sa.Column(sa.Unicode(255))
    name = sa.Column(sa.Unicode(255))
    short_description = sa.Column(sa.Unicode(255))
    id = sa.Column(sa.Integer(), primary_key=True)  # This is defined last so it overrides all others with the same name.
    user_id = sa.Column(sa.Integer(), sa.ForeignKey('???'))
    item_id = sa.Column(sa.Integer(), sa.ForeignKey('???'))

为了避免这种情况,我将我的 Mixins 与初始表定义分开。 IE。当我希望另一个表引用该表时,我会使用 Mixin。

以下内容接近我认为您希望实现的目标。

import sqlalchemy as sa
from sqlalchemy import orm

class UserMixin(object):
    user_id = sa.Column(sa.Integer(), ForeignKey("myuser.id"), index=True)
    user = orm.relationship("MyUser")

class ItemMixin(object):
    item_id = sa.Column(sa.Integer(), ForeignKey("myitem.id"), index=True)
    item = orm.relationship("MyItem")

class OrdersMixin(UserMixin, ItemMixin):
    order_id = sa.Column(sa.Integer(), sa.ForeignKey('myorders.id'))
    user_id = sa.Column(sa.Integer(), sa.ForeignKey('myorders.user_id'))
    item_id = sa.Column(sa.Integer(), sa.ForeignKey('myorders.item_id'))

请注意,在 Mixins 中,我为每一列指定了一个唯一的名称,这样就不会发生冲突,在 OrdersMixin 中,即使我使用的是 UserMixinItemMixin,我正在覆盖 user_id 和 @987654335 @ 列,否则使用 OrdersMixin 的任何内容都会有指向三个不同表的外键,这会混淆自动查询生成器。但它仍然会添加useritem 关系(因为它们被定义为MyOrders 表中原始表的外键,我认为这种关系会起作用)。

然后我会把你的桌子改成这样。

import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class MyUser(Base):
    __tablename__ = "myuser"
    id = sa.Column(sa.Integer(),primary_key=True)
    first_name = sa.Column(sa.Unicode(255))
    last_name  = sa.Column(sa.Unicode(255))

class MyItem(Base):
    __tablename__ = "myitem"
    id = sa.Column(sa.Integer(),primary_key=True)
    name = sa.Column(sa.Unicode(255))
    short_description = sa.Column(sa.Unicode(255))

class MyOrders(Base, UserMixin, OrdersMixin):
    __tablename__ = "myorders"
    id = sa.Column(sa.Integer(),primary_key=True)

原始表定义拥有单独定义它们的列(事实来源),而 Mixins(这种)很适合定义引用,因此后续引用不需要单独定义它们中的每一个。不能将 Mixin 定义为既是参考又是事实来源。鉴于此,与其像OrdersMixin 那样每次都覆盖列,不如只定义一次规范(表格)和一次作为参考(Mixin)。

【讨论】:

    猜你喜欢
    • 2013-11-15
    • 2014-11-08
    • 1970-01-01
    • 2011-11-22
    • 2013-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多