【问题标题】:One-to-one relationship DB Model not working一对一关系数据库模型不起作用
【发布时间】:2018-02-28 10:24:08
【问题描述】:

背景:

我有一个用户可以上传文件的项目。在数据库中,我想存储上传文件的文件路径。

数据库中有 2 个表;一个UsersFileUploadPath。这些表具有一对一的关系。

我目前拥有的 Flask-SQLAlchemy 模型是:

import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////{}'.format(os.path.join(os.getcwd(), "test.db"))
db = SQLAlchemy(app)


class User(db.Model):

    __tablename__ = 'Users'

    userid = db.Column(db.Integer, primary_key=True, autoincrement=True)
    fname = db.Column(db.String(80), nullable=False)
    lname = db.Column(db.String(80), nullable=False)
    phonenum = db.Column(db.String(10), nullable=False)
    email = db.Column(db.String(60), nullable=False)
    password = db.Column(db.String(60), nullable=False)
    validated = db.Column(db.Boolean, default=False)

    child = db.relationship('FileUploadPath', uselist=False, backref='Users')


class FileUploadPath(db.Model):

    __tablename__ = 'FileUploadPath'

    fileUploadID = db.Column(db.Integer, primary_key=True, autoincrement=True)
    w9FilePath = db.Column(db.String(60), nullable=False)
    gcFilePath = db.Column(db.String(60), nullable=False)
    livescanFilePath = db.Column(db.String(60), nullable=False)
    gcaFilePath = db.Column(db.String(60), nullable=False)

    userID = db.Column(db.Integer, db.ForeignKey('Users.userid'))

要创建一个新用户,我使用以下命令:

newuser = User(fname='Bob', lname='Smith', phonenum='6551234567', email='bobsmith@gmail.com', password='fe287943hfbwiqey281')

使用此命令,我希望在FileUploadPath 表中看到userID;当前仅在Users 表中看到创建的用户,但在userID 列中看到NULL

通过查询,我还希望查看与特定用户相关的所有上传文件路径信息。

模型的设置关系是否正确?如果不是,我将如何声明这种关系?

编辑:

我想要做的是将fileUploadID 存储在Users 表中。应该为每个创建的新用户插入FileUploadPath 表中的新行。因为当我创建一个新用户时,FileUploadPath 表中的所有列都将为空;因为新用户还没有上传任何文件。

【问题讨论】:

    标签: python sqlalchemy flask-sqlalchemy


    【解决方案1】:

    表关系看起来没问题,除了 backref='Users' 是一个奇怪的名称选择,因为这会在 FileUploadPath 中添加一个“用户”属性,它不遵循其其余属性的命名约定.我本来希望这是backref='user'

    现在,主要问题是您没有指定FileUploadPath 应该与newuser 关联,因此它没有在FileUploadPath 中插入任何引用newuser ID 的条目。或者您可能插入了一个新的FileUploadPath,您打算将其链接到newuser,尽管您没有在示例中显示该代码。在任何情况下,它都不知道哪个FileUploadPath与哪个User相关联,并且它不会为每个User自动创建一个新的FileUploadPath,所以当你只创建一个新的User时,或者如果您单独创建新的 UserFileUploadPath 但不链接它们,您最终不会得到与新的 User 关联的 FileUploadPath

    因此,您需要做的是在调用 User 时通过添加 child=newFileUploadPath 参数(其中 newFileUploadPath 是已经构造的 FileUploadPath 对象)将 FileUploadPath 链接到 newuser构造函数,或者通过单独构造和添加UserFileUploadPath对象,并设置newuser.child = newFileUploadPathnewFileUploadPath.user = newuser。 (请注意,newFileUploadPath.user 假定我建议的 backref='user' 更改。)

    编辑:这是一个MVCE,它展示了如何为用户添加文件上传。但是最初创建用户时不存在文件上传,这应该是预期的行为。你绝对不应该在FileUploadPath 中有一个包含所有空字符串的条目。此外,如果您计划允许每个用户最多上传 4 次,则需要改为多对一关系,方法是去掉 uselist=False 并将 Users.child 视为一个列表,也许还可以重命名它到childrenuploads

    (请注意,此示例使用普通 SQLAlchemy 而不是 Flask-SQLAlchemy,因此模型定义和数据库设置的某些部分可能与您的实现有所不同。)

    import os
    import sys
    from sqlalchemy import Column, ForeignKey, Integer, String, Boolean
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.orm import relationship, sessionmaker
    from sqlalchemy import create_engine
    
    Base = declarative_base()
    
    class User(Base):
        __tablename__ = 'Users'
    
        userid = Column(Integer, primary_key=True, autoincrement=True)
        fname = Column(String(80), nullable=False)
        lname = Column(String(80), nullable=False)
        phonenum = Column(String(10), nullable=False)
        email = Column(String(60), nullable=False)
        password = Column(String(60), nullable=False)
        validated = Column(Boolean, default=False)
    
        child = relationship('FileUploadPath', uselist=False, backref='user')
    
    
    class FileUploadPath(Base):
        __tablename__ = 'FileUploadPath'
    
        fileUploadID = Column(Integer, primary_key=True, autoincrement=True)
        w9FilePath = Column(String(60), nullable=False)
        gcFilePath = Column(String(60), nullable=False)
        livescanFilePath = Column(String(60), nullable=False)
        gcaFilePath = Column(String(60), nullable=False)
    
        userID = Column(Integer, ForeignKey('Users.userid'))
    
    engine = create_engine('sqlite:///')
    Base.metadata.create_all(engine)
    session_factory = sessionmaker(bind=engine)
    session = session_factory()
    
    # Insert a new user with no uploads:
    newuser = User(fname='Bob', lname='Smith', phonenum='6551234567', email='bobsmith@gmail.com', password='fe287943hfbwiqey281')
    session.add(newuser)
    session.commit()
    
    # Query for users and uploads:
    print(session.query(FileUploadPath).count())
    # 0
    
    print(session.query(User).count())
    # 1
    
    print(session.query(User).one().child)
    # None
    
    # Insert an upload for the user:
    newupload = FileUploadPath(w9FilePath='some_path', gcFilePath='another_path', livescanFilePath='yet_another_path', gcaFilePath='another_path_still', user=newuser)
    session.add(newupload)
    session.commit()
    
    # Again, query for users and uploads:
    print(session.query(FileUploadPath).count())
    # 1
    
    print(session.query(User).one().child)
    # <FileUploadPath object>
    
    print(session.query(User).one().child.w9FilePath)
    # 'some_path'
    

    【讨论】:

    • 查看我对帖子所做的编辑,我试图澄清我到底想要完成什么。以代码格式理解您的逻辑会更容易一些。也许您可以尝试使用我发布的代码并显示一个可视示例?这真的很有帮助,谢谢。
    • 一个用户可以多次上传吗?如果是这样,您不能将fileUploadID 存储在Users 表中。即使每个用户可能只有一次上传,这似乎仍然是一个坏主意,因为您已经说过新用户将有零上传,因此从这个角度来看,让FileUploadPath 包含用户 ID 更有意义,而不是 Users 包含文件上传 ID。 (或者您的意思是 FileUploadPath 仍应包含用户 ID,但您只想能够从 Users 条目中获取文件上传 ID?)
    • 单个用户需要上传4个提交,但这4个提交不必同时发生。他们可以在登录帐户后一次上传一个文件。然后每次用户上传文件时,我想将该路径存储在 fileUploadsPath 表中。
    • 好的,那么您绝对不应该在第一次创建用户时创建一个空的FileUploadPath。只需在文件上传时创建它们。我已经用一个例子更新了我的答案。
    • 感谢您的编辑,这种方式很有意义并且解决了我的问题。
    猜你喜欢
    • 2020-10-16
    • 1970-01-01
    • 1970-01-01
    • 2014-05-06
    • 2016-09-07
    • 1970-01-01
    • 2014-12-18
    相关资源
    最近更新 更多