@app.route
装饰器看起来和之前的有点不一样。 本例中被<
和>
包裹的URL <username>
是动态的。 当一个路由包含动态组件时,Flask将接受该部分URL中的任何文本,并将以实际文本作为参数调用该视图函数。 例如,如果客户端浏览器请求URL /user/susan
,则视图函数将被调用,其参数username
被设置为'susan'
。 因为这个视图函数只能被已登录的用户访问,所以我添加了@login_required
装饰器。all()
来得到所有的结果的查询,或是调用first()
来得到结果中的第一个或者结果集为空时返回None
的查询。 在本视图函数中,我使用了first()
的变种方法,名为first_or_404()
,当有结果时它的工作方式与first()
完全相同,但是在没有结果的情况下会自动发送404 error给客户端。 以这种方式执行查询,我省去检查用户是否返回的步骤,因为当用户名不存在于数据库中时,函数将不会返回,而是会引发404异常。url_for()
调用。 由于个人主页视图函数接受一个动态参数,所以url_for()
函数接收一个值作为关键字参数。 由于这是一个指向当前登录个人主页的链接,我可以使用Flask-Login的current_user
对象来生成正确的URL。Profile
链接就能将你带到自己的个人主页。 此时,虽然没有链接来访问其他用户的主页,但是如果要访问这些页面,则可以在浏览器的地址栏中手动输入网址。 例如,如果你在应用中注册了名为“john”的用户,则可以通过在地址栏中键入 http://localhost:5000/user/john 来查看该用户的个人主页。<hash>
是用户的电子邮件地址的MD5哈希值。 在下面,你可以看到如何生成电子邮件为[email protected]
的用户的Gravatar URL:s
参数来请求不同大小的图片。 例如,要获得我自己128x128像素的头像,该URL是 https://www.gravatar.com/avatar/729e26a2a2c7ff24a71958d4aa4e5f35?s=128 。d
,它让Gravatar为没有向服务注册头像的用户提供的随机头像。 我最喜欢的随机头像类型是“identicon”,它为每个邮箱都返回一个漂亮且不重复的几何设计图片。 如下:User
类新增的avatar()
方法需要传入需求头像的像素大小,并返回用户头像图片的URL。 对于没有注册头像的用户,将生成“identicon”类的随机图片。 为了生成MD5哈希值,我首先将电子邮件转换为小写,因为这是Gravatar服务所要求的。 然后,因为Python中的MD5的参数类型需要是字节而不是字符串,所以在将字符串传递给该函数之前,需要将字符串编码为字节。User
类来返回头像URL的好处是,如果有一天我不想继续使用Gravatar头像了,我可以重写avatar()
方法来返回其他头像服务网站的URL,所有的模板将自动显示新的头像。_
前缀只是一个命名约定,可以帮助我识别哪些模板文件是子模板。include
语句来调用该子模板:migrate
命令的输出表示一切正确运行,因为它显示User
类中的两个新字段已被检测到。 现在我可以将此更改应用于数据库:last_seen
字段开始。 我想要做的就是一旦某个用户向服务器发送请求,就将当前时间写入到这个字段。@before_request
装饰器注册在视图函数之前执行的函数。这是非常有用的,因为现在我可以在一处地方编写代码,并让它在任何视图函数之前被执行。该代码简单地实现了检查current_user
是否已经登录,并在已登录的情况下将last_seen
字段设置为当前时间。我之前提到过,应用应该以一致的时间单位工作,标准做法是使用UTC时区,使用系统的本地时间不是一个好主意,因为如果那么的话,数据库中存储的时间取决于你的时区。最后一步是提交数据库会话,以便将上面所做的更改写入数据库。如果你想知道为什么在提交之前没有db.session.add()
,考虑在引用current_user
时,Flask-Login将调用用户加载函数,该函数将运行一个数据库查询并将目标用户添加到数据库会话中。所以你可以在这个函数中再次添加用户,但是这不是必须的,因为它已经在那里了。about_me
字段中。 让我们开始为它写一个表单类吧:TextAreaField
,这是一个多行输入文本框,用户可以在其中输入文本。 为了验证这个字段的长度,我使用了Length
,它将确保输入的文本在0到140个字符之间,因为这是我为数据库中的相应字段分配的空间。validate_on_submit()
返回True
,我将表单中的数据复制到用户对象中,然后将对象写入数据库。但是当validate_on_submit()
返回False
时,可能是由于两个不同的原因。这可能是因为浏览器刚刚发送了一个GET
请求,我需要通过提供表单模板的初始版本来响应。也可能是这种情况,浏览器发送带有表单数据的POST
请求,但该数据中的某些内容无效。对于该表单,我需要区别对待这两种情况。当第一次请求表单时,我用存储在数据库中的数据预填充字段,所以我需要做与提交相反的事情,那就是将存储在用户字段中的数据移动到表单中,这将确保这些表单字段具有用户的当前数据。但在验证错误的情况下,我不想写任何表单字段,因为它们已经由WTForms填充了。为了区分这两种情况,我需要检查request.method
,如果它是GET
,这是初始请求的情况,如果是POST
则是提交表单验证失败的情况。