【问题标题】:SQLAlchemy union_all and all() returning incorrect number of itemsSQLAlchemy union_all 和 all() 返回不正确的项目数
【发布时间】:2018-10-12 04:54:14
【问题描述】:

由于某种原因,当我使用 SQLAlchemy 的 union_all.all() 时,它返回的项目数不正确。

正如您在下面看到的,我分解了每一个以查看错误所在。有谁知道为什么会发生这种情况?

>>> pn = PostNotification.query.filter_by(notified_id=1)
>>> cn = CommentNotification.query.filter_by(notified_id=1)
>>> pn.count()
4
>>> cn.count()
2
>>> u = pn.union_all(cn)
>>> u.count()
6
>>> all = u.all()
>>> len(all)
5

这是我的两个模型:

class NotificationMixin:
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(150), nullable=False)
    read = db.Column(db.Boolean, default=False)
    created = db.Column(db.DateTime, index=True, default=datetime.utcnow)

    @declared_attr
    def notifier_id(cls):
        return db.Column(db.Integer, db.ForeignKey('user.id'))

    @declared_attr
    def notified_id(cls):
        return db.Column(db.Integer, db.ForeignKey('user.id'))


class PostNotification(db.Model, NotificationMixin):
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
    comment_id = db.Column(db.Integer)

    def __repr__(self):
        return '<PostNotification {}>'.format(self.name)


class CommentNotification(db.Model, NotificationMixin):
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
    comment_id = db.Column(db.Integer, db.ForeignKey('post_comment.id'))

    def __repr__(self):
        return '<CommentNotification {}>'.format(self.name)

更新

Here is a screenshot of the data that represents the two models

当我明确定义列时,使用union_all 时没有问题。当我db.session.query(PostNotification)db.session.query(CommentNotification) 时,它只返回不正确的记录数。

pn = db.session.query(
    PostNotification.id,
    PostNotification.name,
    PostNotification.read,
    PostNotification.created,
    PostNotification.post_id,
    PostNotification.comment_id,
    PostNotification.notifier_id,
    PostNotification.notified_id).filter_by(
        notified_id=1)

cn = db.session.query(
    CommentNotification.id,
    CommentNotification.name,
    CommentNotification.read,
    CommentNotification.created,
    CommentNotification.post_id,
    CommentNotification.comment_id,
    CommentNotification.notifier_id,
    CommentNotification.notified_id).filter_by(
        notified_id=1)

u = pn.union_all(cn).order_by(PostNotification.created.desc())

>>> pn.count()
4
>>> cn.count()
2
u.count()
6
>>> all = u.all()
>>> len(all)
6

问题是我失去了模型,我的关系也消失了。因此,我必须使用这种非常丑陋的解决方法。这只有在您看到 https://i.stack.imgur.com/UHfo7.jpg 中的数据时才有意义。

result = []
for row in u:
    if 'post' in row.name.split('_'):
        n = PostNotification.query.filter_by(id=row.id).first()
        result.append(n)
    if 'comment' in row.name.split('_'):
        n = CommentNotification.query.filter_by(id=row.id).first()
        result.append(n)

现在我的result 按降序排列,两个表通过union_all 合并,我的关系恢复正常。现在的问题是,我显然不能使用 result.paginate,因为result 现在是list

【问题讨论】:

  • 感谢您的建议。我编辑了我的问题。

标签: python sqlalchemy flask-sqlalchemy


【解决方案1】:

联合 u 不是多态的,因为它可以识别哪些行代表 PostNotification 以及哪些 CommentNotification 实体 - 它只是将所有行视为代表主要实体 PostNotification

您在两个表中也有 2 个“相同”通知,即它们具有相同的主键数值。 SQLAlchemy 在查询时根据主键对模型实体进行重复数据删除,as noted here by the author of SQLAlchemy,因此len(u.all()) 返回的结果更少。另一方面,u.count() 计数 在数据库中,因此计数所有行。如果查询属性或超过 1 个实体,则不会发生这种重复数据删除。

【讨论】:

  • 我有什么选择?
  • 您的模型看起来有点像具体的表继承设置,但不是。如果这就是你所追求的,也许看看。 SQLA 有一个 polymorphic_union 就是为了这个。
  • 不确定这是否真的是问题所在,因为我尝试丢失父模型,并在 PostNotificationCommentNotification 中明确定义了列。我仍然收到不正确的记录数。
  • 详细信息没有太大变化。由于相同的原因,它仍然会发生:单个模型实体查询将基于主键进行重复数据删除,并且联合不是多态的,因此具有与 PostNotification 相同 id 的 CommentNotifications 被“重复数据删除”(并且评论通知错误加载作为发布通知)。另一方面,Query.count() 执行计数在数据库中,并看到“重复”(如链接评论中所述)。
  • 我想我想通了……看我的回答。我不太确定它为什么会起作用,但它确实起作用了,呵呵。感谢您的帮助。
【解决方案2】:

看来我想通了。我现在可以直接查询AbstractNotificationdb.session.query(AbstractNotification).all()

from sqlalchemy.ext.declarative import AbstractConcreteBase   


class AbstractNotification(AbstractConcreteBase, db.Model):
    __table__ = None


class NotificationBaseModel:
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(150), nullable=False)
    read = db.Column(db.Boolean, default=False)
    created = db.Column(db.DateTime, index=True, default=datetime.utcnow)

    @declared_attr
    def notifier_id(cls):
        return db.Column(db.Integer, db.ForeignKey('user.id'))

    @declared_attr
    def notified_id(cls):
        return db.Column(db.Integer, db.ForeignKey('user.id'))


class PostNotification(AbstractNotification, NotificationBaseModel):
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
    comment_id = db.Column(db.Integer)

    __mapper_args__ = {
        'polymorphic_identity': 'post_notification',
        'concrete': True
        }

    def __repr__(self):
        return '<PostNotification {}>'.format(self.name)


class CommentNotification(AbstractNotification, NotificationBaseModel):
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
    comment_id = db.Column(db.Integer, db.ForeignKey('post_comment.id'))

    __mapper_args__ = {
        'polymorphic_identity': 'comment_notification',
        'concrete': True
        }

    def __repr__(self):
        return '<CommentNotification {}>'.format(self.name)

【讨论】:

    猜你喜欢
    • 2015-06-04
    • 1970-01-01
    • 2013-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-09
    • 1970-01-01
    相关资源
    最近更新 更多