首页 > 解决方案 > 在烧瓶 sqlalchemy sqlite 关系定义和后续查询方面需要帮助?

问题描述

我已阅读并查看了所有 sqlalchemy 和 flasksqlalchemy 文档,并且我尝试了以下代码的几种不同迭代,但我无法使用 flask-sqlalchemy 方法实现我想要的。

我定义了 3 个表:Users、Downloads 和 ProjectProfiles。

我正在尝试根据“当前用户”提取所有下载和项目配置文件信息。

我可以使用以下标准 SQL 查询来实现这一点:

SELECT u.first_name, u.last_name, pp.project_name, pp.project_number, cd.file_name, cd.original_date
FROM users u
INNER join customer_data cd
ON u.client_id = cd.client_id
INNER join project_profile pp
ON cd.project_id = pp.project_id
WHERE u.id = 3
AND cd.original_date > '2019-11-24'

我的 flask-sqlalchemy 模型定义如下:

class Users(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    client_id = db.Column(db.String(10), index=True)
    first_name = db.Column(db.String(64))
    last_name = db.Column(db.String(64))
    email = db.Column(db.String(120), index=True, unique=True)
    downloads = db.relationship('Downloads', backref='file_owner', lazy='dynamic')

class Downloads(db.Model):
    __tablename__ = 'client_downloads'
    id = db.Column(db.Integer, primary_key=True)
    client_id = db.Column(db.String(10), db.ForeignKey('users.client_id'), index=True)
    project_id = db.Column(db.String(10), index=True)
    file_type = db.Column(db.String(255))
    file_name = db.Column(db.String(255))
    file_extension = db.Column(db.String(5))
    file_directory = db.Column(db.TEXT())
    original_date = db.Column(db.DateTime)
    project_info = db.relationship('ProjectProfiles', foreign_keys='ProjectProfiles.project_id', backref='pinfo', lazy='dynamic')

class ProjectProfiles(db.Model):
    __tablename__ = 'project_profile'
    id = db.Column(db.Integer, primary_key=True)
    project_id = db.Column(db.String(10), db.ForeignKey('client_downloads.project_id'), index=True)
    project_name = db.Column(db.String(40))
    project_number = db.Column(db.String(30))
    client_id = db.Column(db.String(10))
    created_date = db.Column(db.DateTime)
    modified_date = db.Column(db.DateTime)

当使用以下命令使用flask shell进行测试时,我可以获得特定用户的下载,但是如果我尝试获取与用户/下载关联的项目配置文件,我似乎无法访问该信息:

获取下载的 Flask shell 命令:

[2019-12-02 13:49:04,444] INFO in __init__: Example startup
Python 3.6.8 (default, Aug  7 2019, 17:28:10)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
App: app [testing]
Instance: /var/www/testing/instance
>>> from app import db
>>> from app.models import Users, Downloads, ProjectProfiles
>>> from datetime import datetime, timedelta
>>> u = Users.query.get(2)
>>> curr_date = datetime.utcnow()
>>> diff_date = curr_date - timedelta(weeks=2)
>>> dloads = u.downloads.filter(Downloads.original_date > diff_date).all()

dloads 的结果:

>>> dloads
[<Downloads 240169>]
>>> for d in dloads:
...     print("project_id:", d.project_id)
...
project_id: 20635

从 dloads 访问 project_info 定义/关系时的结果:

>>> for d in dloads:
...     print(d.project_info)
...
SELECT project_profile.id AS project_profile_id, project_profile.project_id AS 
project_profile_project_id, project_profile.projname AS project_profile_projname, 
project_profile.projnumber AS project_profile_projnumber, project_profile.client_id AS 
project_profile_client_id, project_profile.created_date AS project_profile_created_date, 
project_profile.modified_date AS project_profile_modified_date
FROM project_profile
WHERE ? = project_profile.project_id

尝试将 projs 变量设置为 dloads.project_info 时出错:

>>> projs = dloads.project_info.query.all()
Traceback (most recent call last): File "<console>", line 1, in <module>
AttributeError: 'list' object has no attribute 'project_info'
>>>

如果我首先从下载返回单个值并专门过滤/加入 project_ids,则成功结果:

>>> d = Downloads.query.get(2)
>>> d
<Downloads 2>
>>> projs = d.project_info.filter(ProjectProfiles.project_id == d.project_id).all()
>>> projs
[<ProjectProfiles 3779>]
>>> for p in projs:
...     print("project_name:", p.project_name)
...
project_name: Test Project Profile

当前用于下载的烧瓶路线定义:

@app.route('/testing', methods=['GET', 'POST'])
@app.route('/testing/index', methods=['GET', 'POST'])
@login_required
def index():
    curr_date = datetime.utcnow()
    diff_date = curr_date - timedelta(weeks=52)
    downloads = current_user.downloads.filter(Downloads.original_date > diff_date).all()
    return render_template('index.html', downloads=downloads, user=current_user)

即使在上面的路由定义中,从技术上讲,我只返回特定于下载的字段,而不是像我习惯使用上面显示的 SQL 查询那样返回下载和用户数据的组合。

我想做的是定义一个对象/变量,它将下载和项目信息一起返回到由当前用户过滤的单个对象/变量中,如上所示!

任何帮助深表感谢!

编辑 - 根据接受的答案,我想出了以下对我有用的方法

downloads = db.session.query(Downloads).join(Users, ProjectProfiles).filter(Downloads.original_date > diff_date, Users.id == current_user.id)

标签: pythonflasksqlalchemyflask-sqlalchemy

解决方案


关于错误,我们必须考虑到 dload 是一个列表而不是单个下载。此外,project_info 也是一个列表。因此,根据您的逻辑,您需要获取列表中的第一个元素:

projs = dloads[0].project_info[0].query.all()

如果要在一个结果中从不同表中获取结果,则结果是从不同表中选择的属性列表(不再是单个 sqlalchemy 对象)。至少如果你想有一个单一的声明。例如:

combinedresult = db.session.query(ProjectProfiles.id, Downloads.id, 
    Users.id).filter(Downloads.original_date > diff_date, Users.id==1).all()

为项目配置文件、下载、受日期约束限制的用户和特定用户选择 ID。


推荐阅读