10

说,我们有以下关系:

  • 一个人可以有多个电子邮件地址
  • 电子邮件服务提供商可以(显然)提供多个电子邮件地址

所以,这是多对多的关系。我有三个表:电子邮件、提供商和用户。电子邮件有两个用于提供者和用户的外国 ID。

现在,给定一个特定的人,我想打印所有电子邮件提供商及其为该人托管的电子邮件地址(如果存在)。(如果此人在 Gmail 中没有电子邮件,我仍然希望 Gmail 出现在结果中。我相信否则我只需要一个左内连接来解决这个问题。)

我想出了如何使用以下子查询来做到这一点(遵循 sqlalchemy 教程):

email_subq = db.session.query(Emails).\
                filter(Emails.user_id==current_user.id).\
                subquery()

provider_and_email = db.session.query(Provider, email_subq).\
                outerjoin(email_subq, Provider.emails).\
                all()

这工作正常(它返回一个 4 元组(Provider, user_id, provider_id, email_address),我想要的所有信息),但我后来发现这不是使用 FlaskBaseQuery类,所以paginationFlask-SQLAlchemy 提供的那个不起作用。显然db.session.query()不是 Flask-SQLAlchemy Query 实例。

我尝试这样做,Emails.query.outerjoin[...]但只返回电子邮件表中的列,尽管我想要提供者信息和电子邮件。

我的问题:我怎样才能用 Flask-SQLAlchemy 做同样的事情,这样我就不必重新实现已经存在的分页?


我想此时最简单的选择是实现我自己的分页功能,但我很想知道是否有另一种正确的方法来做到这一点。

4

7 回答 7

12

我不确定这是否最终会成为长期解决方案,它并没有直接解决我对不使用 Flask-SQLAlchemy 的 BaseQuery 的担忧,但完成我想要的最简单的方法是重新实现分页功能。

而且,事实上,使用原始的 Flask-SQLAlchemy 例程很容易做到这一点:

def paginate(query, page, per_page=20, error_out=True):
    if error_out and page < 1:
        abort(404)
    items = query.limit(per_page).offset((page - 1) * per_page).all()
    if not items and page != 1 and error_out:
        abort(404)

    # No need to count if we're on the first page and there are fewer
    # items than we expected.
    if page == 1 and len(items) < per_page:
        total = len(items)
    else:
        total = query.order_by(None).count()

    return Pagination(query, page, per_page, total, items)

paginate根据第376 行附近的函数修改: https ://github.com/mitsuhiko/flask-sqlalchemy/blob/master/flask_sqlalchemy.py

于 2013-03-31T21:10:43.230 回答
4

您的问题是如何将 Flask-SQLAlchemy 的分页与常规 SQLAlchemy 查询一起使用。

由于 Flask-SQLAlchemy 的 BaseQuery 对象没有自己的状态,并且是从 SQLAlchemy 的 Query 派生的,并且实际上只是方法的容器,因此您可以使用以下 hack:

from flask.ext.sqlalchemy import BaseQuery
def paginate(sa_query, page, per_page=20, error_out=True):
  sa_query.__class__ = BaseQuery
  # We can now use BaseQuery methods like .paginate on our SA query
  return sa_query.paginate(page, per_page, error_out)

要使用:

@route(...)
def provider_and_email_view(page):
  provider_and_email = db.session.query(...) # any SQLAlchemy query
  paginated_results = paginate(provider_and_email, page)
  return render_template('...', paginated_results=paginated_results)

*编辑:

请小心执行此操作。paginate正如其他答案中所见,这实际上只是一种避免复制/粘贴函数的方法。请注意,BaseQuery 没有__init__方法。看看设置self.__class__其他东西有多危险?.

*编辑2:

如果 BaseQuery 有一个__init__,您可以使用 SA 查询对象构造一个,而不是破解.__class__.

于 2013-08-16T22:14:25.083 回答
3

嘿,我在这里找到了一个快速解决方法:

provider_and_email = Provider.query.with_entities(email_subq).\
            outerjoin(email_subq, Provider.emails).paginate(page, POST_PER_PAGE_LONG, False)
于 2013-08-01T14:04:53.187 回答
3

我目前正在使用这种方法:

query = BaseQuery([Provider, email_subq], db.session())

创建我自己的BaseQuery. dbSqlAlchemy实例。

更新:正如@a​​filbert 建议你也可以这样做:

query = BaseQuery(provider_and_email.subquery(), db.session())
于 2016-03-15T11:03:05.487 回答
0

你如何用 SQLAlchemy 初始化你的应用程序?

可能您当前的 SQLAlchemy 连接与 flask.ext.sqalchemy 无关,而您使用原始 sqlalchemy

检查本教程并检查您的导入,它们确实来自 flask.ext.sqlalchemy

http://pythonhosted.org/Flask-SQLAlchemy/quickstart.html#a-minimal-application

于 2013-03-31T10:41:37.273 回答
0

您可以尝试使用结果对列表进行分页。

my_list = [my_list[i:i + per_page] for i in range(0, len(my_list), per_page)][page]
于 2019-02-13T14:03:52.993 回答
-1

我可能是错的,但我认为你的问题可能是 .all()。通过使用它,您将获得一个列表,而不是一个查询对象。

尝试将其关闭,并将您的查询传递给分页方法,如下所示(为了清楚起见,我省略了所有子查询细节):

email_query = db.session.query(Emails).filter(**filters)
email_query.paginate(page, per_page)
于 2013-03-31T07:23:56.910 回答