将用户表示为 JSON 对象
实施 API 时要考虑的第一个方面是决定其资源表示形式。 我要实现一个用户类型的 API,因此我需要决定的是用户资源的表示形式。 经过一番头脑风暴,得出了以下 JSON 表示形式:
{
"id": 123,
"username": "susan",
"password": "my-password",
"email": "susan@example.com",
"last_seen": "2017-10-20T15:04:27Z",
"about_me": "Hello, my name is Susan!",
"post_count": 7,
"follower_count": 35,
"followed_count": 21,
"_links": {
"self": "/api/users/123",
"followers": "/api/users/123/followers",
"followed": "/api/users/123/followed",
"avatar": "https://www.gravatar.com/avatar/..."
}
}
许多字段直接来自用户数据库模型。 password
字段的特殊之处在于,它仅在注册新用户时才会使用。 回顾 第五章 ,用户密码不存储在数据库中,只存储一个散列字符串,所以密码永远不会被返回。 email
字段也被专门处理,因为我不想公开用户的电子邮件地址。 只有当用户请求自己的条目时,才会返回 email
字段,但是当他们检索其他用户的条目时不会返回。 post_count
, follower_count
和 followed_count
字段是“虚拟”字段,它们在数据库字段中不存在,提供给客户端是为了方便。 这是一个很好的例子,它演示了资源表示不需要和服务器中资源的实际定义一致。
请注意 _links
部分,它实现了超媒体要求。 定义的链接包括指向当前资源的链接,用户的粉丝列表链接,用户关注的用户列表链接,最后是指向用户头像图像的链接。 将来,如果我决定向这个 API 添加用户动态,那么用户的动态列表链接也应包含在这里。
JSON 格式的一个好处是,它总是转换为 Python 字典或列表的表示形式。 Python 标准库中的 json
包负责 Python 数据结构和 JSON 之间的转换。因此,为了生成这些表示,我将在 User
模型中添加一个名为 to_dict()
的方法,该方法返回一个 Python 字典:
app/models.py :User 模型转换成表示。
from flask import url_for
# ...
class User(UserMixin, db.Model):
# ...
def to_dict(self, include_email=False):
data = {
'id': self.id,
'username': self.username,
'last_seen': self.last_seen.isoformat() + 'Z',
'about_me': self.about_me,
'post_count': self.posts.count(),
'follower_count': self.followers.count(),
'followed_count': self.followed.count(),
'_links': {
'self': url_for('api.get_user', id=self.id),
'followers': url_for('api.get_followers', id=self.id),
'followed': url_for('api.get_followed', id=self.id),
'avatar': self.avatar(128)
}
}
if include_email:
data['email'] = self.email
return data
该方法一目了然,只是简单地生成并返回用户表示的字典。正如我上面提到的那样, email
字段需要特殊处理,因为我只想在用户请求自己的数据时才包含电子邮件。 所以我使用 include_email
标志来确定该字段是否包含在表示中。
注意一下 last_seen
字段的生成。 对于日期和时间字段,我将使用 ISO 8601 格式,Python 的 datetime
对象可以通过 isoformat()
方法生成这样格式的字符串。 但是因为我使用的 datetime
对象的时区是 UTC,且但没有在其状态中记录时区,所以我需要在末尾添加 Z
,即 ISO 8601 的 UTC 时区代码。
最后,看看我如何实现超媒体链接。 对于指向应用其他路由的三个链接,我使用 url_for()
生成 URL(目前指向我在 app/api/users.py 中定义的占位符视图函数)。 头像链接是特殊的,因为它是应用外部的 Gravatar URL。 对于这个链接,我使用了与渲染网页中的头像的相同 avatar()
方法。
to_dict()
方法将用户对象转换为 Python 表示,以后会被转换为 JSON。 我还需要其反向处理的方法,即客户端在请求中传递用户表示,服务器需要解析并将其转换为 User
对象。 以下是实现从 Python 字典到 User
对象转换的 from_dict()
方法:
app/models.py :表示转换成 User 模型。
class User(UserMixin, db.Model):
# ...
def from_dict(self, data, new_user=False):
for field in ['username', 'email', 'about_me']:
if field in data:
setattr(self, field, data[field])
if new_user and 'password' in data:
self.set_password(data['password'])
本处我决定使用循环来导入客户端可以设置的任何字段,即 username
, email
和 about_me
。 对于每个字段,我检查它是否存在于 data
参数中,如果存在,我使用 Python 的 setattr()
在对象的相应属性中设置新值。
password
字段被视为特例,因为它不是对象中的字段。 new_user
参数确定了这是否是新的用户注册,这意味着 data
中包含 password
。 要在用户模型中设置密码,需要调用 set_password()
方法来创建密码哈希。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论