SQLAlchemy union_all и all () возвращают неверное количество элементов - PullRequest
0 голосов
/ 08 октября 2018

По какой-то причине, когда я использую 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)

ОБНОВЛЕНИЕ :

Вот снимок экрана данных, которые представляют две модели

Когда я явно определяю столбцы, при использовании 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.

Ответы [ 2 ]

0 голосов
/ 10 октября 2018

Похоже, я понял это.Теперь я могу запросить 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)
0 голосов
/ 08 октября 2018

Объединение u не является полиморфным в том смысле, что оно распознает, какие строки представляют PostNotification, а какие CommentNotification сущности - оно просто обрабатывает все строки как представляющие первичную сущность PostNotification.

Также бывает, что в обеих таблицах есть 2 «одинаковых» уведомления, т.е. они имеют одинаковое числовое значение для первичного ключа.SQLAlchemy дедуплицирует сущности модели на основе первичного ключа при запросе, , как отмечено здесь автором SQLAlchemy , и поэтому len(u.all()) возвращает меньше результатов.u.count(), с другой стороны, считает в базе данных и, таким образом, считает все строки.Эта дедупликация не выполняется при запросе атрибутов или более чем 1 объекта.

...