【问题标题】:SQLAlchemy One-to-(Some of Many) relationshipSQLAlchemy 一对(多的一些)关系
【发布时间】:2020-11-01 14:05:14
【问题描述】:

我目前有一个 SQLAlchemy 模型User,它有两种相关的测量值,体重和身高。这些作为外键添加。

class User(UserMixin, db.Model):
    __tablename__ = "user"
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(100), unique=True)
    password = db.Column(db.String(100))
    name = db.Column(db.String(1000))
    weight = db.relationship("Weight", cascade="all,delete")
    height = db.relationship("Height", cascade="all,delete")

class Weight(db.Model):
    __tablename__ = "weight"
    id = db.Column(db.Integer, primary_key=True)
    date = db.Column(db.DateTime)
    weight = db.Column(db.Float)
    user_id = db.Column(db.Integer, db.ForeignKey("user.id", ondelete="CASCADE"))

class Height(db.Model):
    __tablename__ = "height"
    id = db.Column(db.Integer, primary_key=True)
    date = db.Column(db.DateTime)
    height = db.Column(db.Float)
    user_id = db.Column(db.Integer, db.ForeignKey("user.id", ondelete="CASCADE"))

我希望能够添加更多类型的测量,而不必每次都重新构建数据库结构。因此我考虑使用一个通用的Measurement 模型,它有一个用于测量类型的列(不是Enum,所以我可以轻松添加新类型):

class User(UserMixin, db.Model):
    __tablename__ = "user"
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(100), unique=True)
    password = db.Column(db.String(100))
    name = db.Column(db.String(1000))
    measurements = db.relationship("Measurement", cascade="all,delete")

    @property
    def weight(self):
        pass    # <-- No idea what to do here

    @property
    def height(self):
        pass    # <-- No idea what to do here

class Measurement(db.Model):
    __tablename__ = "measurement"
    id = db.Column(db.Integer, primary_key=True)
    date = db.Column(db.DateTime)
    value = db.Column(db.Float)
    user_id = db.Column(db.Integer, db.ForeignKey("user.id", ondelete="CASCADE"))
    measurement_type_id = db.Column(db.Integer, db.ForeignKey('measurement_type.id'))
    measurement_type = db.relationship("MeasurementType")

class MeasurementType(db.Model):
    __tablename__ = "measurement_type"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))
    unit = db.Column(db.String(100), nullable=True)

有了这个新模型,我如何只获得例如重量?对于当前的模型,就这么简单,current_user 是当前登录的用户:

current_user.weight

但我想它必须是这样的:

current_user.measurements.filter(Measurement.measurement_type == "Weight")

(这不起作用,因为current_user.measurements 返回一个列表。)

同样,我如何为测量值添加新值?目前我这样做:

current_user.weight.append(Weight(date=date, weight=weight))
db.session.commit()

我是否基本上需要复制 relationship 的低级实现,以便按用户 ID 和测量类型两件事进行过滤?

或者我可以使用association proxy 以某种方式实现这一点吗?或者(正确地)使用relationshipprimaryjoin 参数?

【问题讨论】:

    标签: python sqlalchemy flask-sqlalchemy model-associations


    【解决方案1】:

    我发现实现此目的的最简单方法是在关系上使用动态延迟加载,正如在 this answersimilar question 上所推荐的那样。这样返回类型就不再是一个简单的列表,而是一个 SQL 对象。这也使用简单的字符串作为测量类型,而不是查找表。

    class User(UserMixin, db.Model):
        __tablename__ = "user"
        id = db.Column(db.Integer, primary_key=True)
        email = db.Column(db.String(100), unique=True)
        password = db.Column(db.String(100))
        name = db.Column(db.String(1000))
        measurements = db.relationship("Measurement", cascade="all,delete", lazy="dynamic")
    
    
    class Measurement(db.Model):
        __tablename__ = "measurement"
        id = db.Column(db.Integer, primary_key=True)
        m_type = db.Column(db.String(20))
        date = db.Column(db.DateTime)
        value = db.Column(db.Float)
        user_id = db.Column(db.Integer, db.ForeignKey("user.id", ondelete="CASCADE"))
    

    之后,您可以对测量值使用常规过滤方法:

    list(current_user.measurements.filter_by(m_type="weight"))
    

    而且添加新行也很简单:

    current_user.measurements.append(Measurement(m_type="weight", date=date, value=weight))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-06-15
      • 2021-08-31
      • 1970-01-01
      • 2019-12-06
      • 2020-05-16
      • 2014-05-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多