说,我们有以下关系:
所以,这是一对多关系。我有三个表:电子邮件,提供商和用户。电子邮件有两个外国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()
这样可行(它返回(Provider, user_id, provider_id, email_address)
的4元组,我想要的所有信息),但我后来发现这是不使用Flask {{1} } class,因此Flask-SQLAlchemy提供的BaseQuery
不起作用。显然pagination
不是Flask-SQLAlchemy Query实例。
我尝试db.session.query()
,但只返回电子邮件表格中的列,但我想要提供商信息和电子邮件。
我的问题:我如何使用Flask-SQLAlchemy做同样的事情,这样我就不必重新实现已存在的分页?
我认为此时最简单的选择是实现我自己的分页功能,但我很想知道是否还有其他正确的方法。
答案 0 :(得分:8)
我不确定这是否会成为长期解决方案,并不能直接解决我对不使用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)
从第376行附近的paginate
函数修改:https://github.com/mitsuhiko/flask-sqlalchemy/blob/master/flask_sqlalchemy.py
答案 1 :(得分:3)
您的问题是如何在常规SQLAlchemy查询中使用Flask-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__
方法。请参阅How dangerous is setting self.__class__
to something else?。
* EDIT2:
如果BaseQuery有__init__
,您可以使用SA查询对象构建一个,而不是攻击.__class__
。
答案 2 :(得分:3)
我目前正在使用这种方法:
query = BaseQuery([Provider, email_subq], db.session())
创建我自己的BaseQuery
。 db
是SqlAlchemy
个实例。
更新:作为@afilbert suggests,你也可以这样做:
query = BaseQuery(provider_and_email.subquery(), db.session())
答案 3 :(得分:2)
嘿,我在这里找到了一个快速解决方法:
provider_and_email = Provider.query.with_entities(email_subq).\
outerjoin(email_subq, Provider.emails).paginate(page, POST_PER_PAGE_LONG, False)
答案 4 :(得分:0)
我可能错了,但我认为你的问题可能是.all()。通过使用它,您将获得一个列表,而不是一个查询对象。
尝试将其关闭,然后将查询传递给分页方法(为清楚起见,我将所有子查询详细信息保留下来):
email_query = db.session.query(Emails).filter(**filters)
email_query.paginate(page, per_page)
答案 5 :(得分:0)
如何使用SQLAlchemy初始化您的应用程序?
可能你当前的SQLAlchemy连接与flask.ext.sqalchemy无关,你使用原始sqlalchemy
查看本教程并检查您的导入,它们确实来自于flask.ext.sqlalchemy
http://pythonhosted.org/Flask-SQLAlchemy/quickstart.html#a-minimal-application
答案 6 :(得分:0)
您可以尝试用结果对列表进行分页。
my_list = [my_list[i:i + per_page] for i in range(0, len(my_list), per_page)][page]