简体   繁体   English

SQLAlchemy union_all和all()返回错误的项目数

[英]SQLAlchemy union_all and all() returning incorrect number of items

For some reason when I'm using SQLAlchemy's union_all and .all() , it's returning the incorrect number of items. 由于某些原因,当我使用SQLAlchemy的union_allunion_all .all() ,它返回的项目数不正确。

As you can see below, I broke each one down to see where the error was. 正如您在下面看到的那样,我将每个细分分解,以查看错误所在。 Does anyone have any idea why this would be happening? 有谁知道为什么会这样?

>>> 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

Here are my two models: 这是我的两个模型:

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)

UPDATE : 更新

Here is a screenshot of the data that represents the two models 这是代表两个模型的数据的屏幕截图

When I define the columns explicitly, there is no issue when I'm using union_all . 当我显式定义列时,使用union_all不会有问题。 It only returns the incorrect amount of records when I db.session.query(PostNotification) and db.session.query(CommentNotification) . 仅在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

The problem with this, is I lose the model, and my relationships are gone. 问题在于,我丢失了模型,而我的关系也消失了。 Therefore, I have to use this very ugly workaround. 因此,我必须使用此非常丑陋的解决方法。 This only makes sense if you see the data in https://i.stack.imgur.com/UHfo7.jpg . 仅当您在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)

Now my result is in descending order, both tables are combined via union_all , and my relationships are back in tact. 现在,我的result按降序排列,两个表都通过union_all合并union_all ,并且我的关系恢复了union_all The problem now is, I obviously can't use result.paginate, because result is now a list . 现在的问题是,我显然不能使用result.paginate,因为result现在是一个list

The union u is not polymorphic in the sense that it'd recognize which rows represent PostNotification and which CommentNotification entities – it simply treats all rows as representing the primary entity PostNotification . 联合u在识别出哪些行代表PostNotification和哪个CommentNotification实体的意义上不是多态的,它只是将所有行都视为代表主要实体PostNotification

It also happens that you have 2 "identical" notifications in both tables, ie they have the same numeric value for primary key. 也可能在两个表中都有2个“相同”的通知,即它们的主键数值相同。 SQLAlchemy deduplicates model entities based on primary key when querying, as noted here by the author of SQLAlchemy , and so len(u.all()) returns fewer results. SQLAlchemy在查询时会基于主键对模型实体进行重复数据删除, 正如SQLAlchemy的作者在此处指出的那样 ,因此len(u.all())返回的结果较少。 u.count() on the other hand counts in the database and so counts all rows. u.count()数据库中计数,因此对所有行进行计数。 This deduplication does not take place if querying attributes or more than 1 entity. 如果查询属性或多于1个实体,则不会发生重复数据删除。

Looks like I figured it out. 好像我想通了。 I can now query AbstractNotification directly db.session.query(AbstractNotification).all() 我现在可以直接查询AbstractNotification db.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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM