为什么在SQLAlchemy数据库中使用self而不是self.last_seen(Flask)

0 投票
3 回答
520 浏览
提问于 2025-04-18 15:13

在这段代码中,每当用户使用网站时,last_seen 字段会被更新为当前时间。不过,在调用数据库的时候,Minuel Grindberg(“Flask Web Development”)使用了 self 而不是 self.last_seen,这让我感到困惑。我明白面向对象编程(OOP)的基本原则,也(以为)我理解 self 是什么(指的是正在创建的对象),但我不明白为什么在最后一行 db.session.add(self) 中不使用 self.last_seen?完整代码如下……

class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(64), unique=True, index=True)
    username = db.Column(db.String(64), unique=True, index=True)
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    password_hash = db.Column(db.String(128))
    confirmed = db.Column(db.Boolean, default=False)
    name = db.Column(db.String(64))
    location = db.Column(db.String(64))
    about_me = db.Column(db.Text())
    member_since = db.Column(db.DateTime(), default=datetime.utcnow)
    last_seen = db.Column(db.DateTime(), default=datetime.utcnow)

def ping(self):
    self.last_seen = datetime.utcnow()
    db.session.add(self)

看起来很简单,我相信确实是这样,但显然我漏掉了什么,或者还没有学到应该学的东西。如果我知道该用什么关键词去搜索答案,我肯定会去找,但我甚至不知道除了 Python OOP 的原则之外该搜索什么,因为我以为我已经理解了(我确实复习过)。任何帮助都将非常感激,因为这让我很烦恼,哈哈。

3 个回答

0

谢谢,我终于明白了!

你是说,把包含整个模型和 SQLAlchemy 知道该用什么信息、该丢弃什么信息的 self 发送过去,比仅仅发送一些信息要好。因为如果只发一些信息,SQLAlchemy 可能找不到对应的行。

这个 self 包含了 self.idself.nameself.email,这样 SQLAlchemy 就能找到它需要的行和数据。

另一方面,self.last_seen 只包含一个日期时间对象,没有其他信息。

所以,这实际上就像你在编辑一个实例。

假设我们创建了一个名为 user 的实例。

user = User(name="Eni", email="eni@gmail.com")

我们用 db.session.add(user) 把这个用户添加进去,然后提交。

但突然我们需要更改用户的邮箱,像这样 user.email = "eni1234@gmail.com"

当我们想把这些更改添加到数据库时,我们不是只添加 db.session.add(user.email),而是要添加整个实例,这样修改才能生效,像这样: db.session.add(user)

太好了,非常感谢!

0

IanAuld说得对——不过我会尽量详细解释一下。

我们假装自己是SQLAlchemy,想象一下我们是db.session.add这个方法。

self.last_seen是一个datetime对象,假设我们在家里,突然有一个信封从门口送进来,上面写着db.session.add。太好了,这就是我们,于是我们打开信封,里面只有一句话:2014-07-29,没别的了。我们知道需要把这个信息放到文件柜里,但我们没有足够的信息来做这件事。我们只知道这是一个datetime,但不知道它属于哪个User,甚至不知道它是否真的属于某个User,所以我们就卡住了。

如果接下来送来的东西是一个包裹,还是写着db.session.add,我们再次打开——这次里面是一个小模型,代表一个User,上面有名字、邮箱,甚至还有一个last_seen的时间。现在就简单多了——我可以直接去文件柜看看之前是否已经有这个用户的信息,如果有,我可以做一些修改让它们一致,或者如果是新用户,就直接把这个信息放进去。

这就是区别——使用ORM模型时,你传递的是完整的用户、产品或其他对象,SQLAlchemy知道这是一个db.Model,因此可以通过检查它的细节来知道如何处理它、放在哪里。

1

他正在把更新后的模型添加到数据库中。模型发生了变化,所以 db.session.add() 会在后台更新正确的行。我认为 SQLAlchemy 不会允许你仅仅添加模型的某个属性,因为它不知道要更新哪一行。

也许举个例子会更清楚。我们来看下面这个模型:

class User(db.model):
    __tablename__ = 'User'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(25))

现在,这个模型有两个非常重要的属性,用于在数据库中插入或更新数据。一个是表名,另一个是 ID。所以,如果我们想用普通的 SQL 把这个模型添加到数据库中,我们需要做类似这样的操作:

INSERT INTO User (name) VALUES ('Some string');

这大致就是当你对一个新模型使用 db.session.add() 时发生的事情。要更新我们的模型,我们需要做类似这样的操作:

UPDATE User
SET name='Some other String'
WHERE id=1;

现在,如果你只把模型的一个属性传给 SQLAlchemy,它怎么知道你想要添加到哪个表,或者哪一行应该被更改呢?如果你只是把 self.name 传给 db.session.add(),那么查询的结果可能会是这样的:

UPDATE  # There is no way to know the table
SET name='Some other String'
WHERE  ; # There is no way to know which row needs to be changed

如果你这样做,SQLAlchemy 很可能会抛出一个异常。至于为什么它不能从 self 推断出模型,这可能超出了一个 StackOverflow 问题的范围。

撰写回答