Flask SQLAlchemy - упорядочивает результаты по весу / баллу, основываясь на количестве совпадающих объектов «многие ко многим»

1

Учитывая следующие модели:

class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode(255))
    taxonomy = db.Column(db.Unicode(), nullable=False)

    created_at = db.Column(db.DateTime, default=db.func.now())
    updated_at = db.Column(db.DateTime, default=db.func.now(), onupdate=db.func.now())

    __mapper_args__ = {
        'polymorphic_identity': 'tag',
        'polymorphic_on': taxonomy,
        'order_by': name
    }


class FooTag(Tag):
    id = db.Column(db.Integer, db.ForeignKey('tag.id'), primary_key=True)

    __mapper_args__ = {
        'polymorphic_identity': 'foo'
    }


class BarTag(Tag):
    id = db.Column(db.Integer, db.ForeignKey('tag.id'), primary_key=True)

    __mapper_args__ = {
        'polymorphic_identity': 'bar',
    }


class Profile(db.Model):
    foo_tags = db.relationship(
        "FooTag",
        secondary=profiles_tags,
        secondaryjoin="and_(Tag.id==profiles_tags.c.tag_id, Tag.taxonomy=='foo')",
        uselist=True,
        lazy='dynamic'
    )

    bar_tags = db.relationship(
        "BarTag",
        secondary=profiles_tags,
        secondaryjoin="and_(Tag.id==profiles_tags.c.tag_id, Tag.taxonomy=='bar')",
        uselist=True,
        lazy='dynamic'
    )

Есть ли в любом случае, что я могу фильтровать объекты Profile с помощью набора Tag s, а затем заказывать результаты по количеству соответствующих Tag s? то есть.

foo_tags=[2, 4, 6]
bar_tags=[8, 22, 14] 

results = Profile.join(Profile.foo_tags).filter(FooTag.id.in_(foo_tags)).\
    join(Profile.bar_tags).filter(BarTag.id.in_(bar_tags)).\
    paginate(1, 20) 

Где порядок results определяется количеством совпадений foo_tags/bar_tags на каждом возвращенном объекте?

Это Python==2.7, Flask==0.12.2, SQLAlchemy==1.1.11, Flask-SQLAlchemy==2.3.2

Теги:
sqlalchemy

1 ответ

1
Лучший ответ

Поскольку вы используете объединенное наследование, и поэтому FooTag и BarTag ссылаются на один и тот же домен id Tag, нет необходимости отдельно присоединяться и фильтровать на основе foo_tags и bar_tags. Вместо этого вы могли бы присоединиться к Profile и Tag или даже просто Profile и profiles_tags, фильтр и группу с помощью ключа, такого как Profile.id, если он имеет такой первичный ключ (пример в вопросе отсутствует в этом отношении):

results = Profile.query.\
    join(profiles_tags).\
    filter(profiles_tags.c.tag_id.in_(foo_tags + bar_tags)).\
    group_by(Profile.id).\
    order_by(func.count(), Profile.id)

Идентификатор профиля включается как тай-брейк в заказе, так что вы получаете детерминированную разбивку на страницы.

  • 0
    Это прекрасное решение, теперь кажется таким очевидным. Спасибо!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню