RESTful 金字塔视图配置中的设计
RESTful design in Pyramid View Configuration
这是我第一次尝试设计网络应用程序。我为 SQLAlchemy 数据库创建了一个 CRUD 操作 API。我现在正在尝试使用 Pyramid 设计一个 RESTful 网络框架来支持这个数据库及其所有实体。
我一直在苦苦思索如何PUT(更新)。我知道我还需要加入某些条件(例如代码 202)。我不确定如何使用 PUT 处理对象替换。视图配置中的方法有点混乱,我相信我正在关注数据库 CRUD 操作。
其次,我认为所有资源都必须通过{id}
查找。我相信我在 get_user
方法中是正确的。 User_id
在 db
中保存了用户的所有信息...因此,如果 user
被 id
查找,那么我不需要拥有所有其他信息(参见 delete_user
方法)。
我已经阅读了一些漂亮的 tutorials on designing a RESTful 设计,但考虑到我是自学的,我仍在苦苦挣扎。非常感谢您的帮助!
下面是一些显示问题的代码示例:
_ init _.py (URI):
config.add_route('post_user', '/users') # POST (HTTP) / CREATE (CRUD)
config.add_route('get_user', '/users/{id}') #GET (HTTP) / RETRIEVE (CRUD)
config.add_route('put_user', '/users/{id}') # PUT (HTTP) / UPDATE (CRUD)
config.add_route('delete_user', '/users/{id}') # DELETE (HTTP) / DELETE (CRUD)
views.py(查看配置 -- RESTful):
@view_defaults(renderer='json')
class RESTView(object):
api = ConvenienceAPI() # CRUD API for database
def __init__(self, request):
self.request = request
@view_config(route_name='get_user', request_method='GET')
def get_user(self):
user_id = int(request.matchdict['user_id'])
user = api.retrieve_user('user_id')
if user is None:
raise HTTPNotFound()
else:
return user
@view_config(route_name='post_user', request_method='POST')
def post_user(self):
username = request.matchdict['username']
password = request.matchdict['password']
firstname = request.matchdict['firstname']
lastname = request.matchdict['lastname']
email = request.matchdict['email']
new_user = api.create_user('username', 'password', 'firstname', 'lastname', 'email')
return Response{'new_user': user}
@view_config(route_name='put_user', request_method='PUT')
def put_user(self):
user = request.matchdict['user_id']
new_username = # is this pointing to another URL ????
updated_user = api.update_user('username', 'new_username')
return Response{status='202 Accepted'}
@view_config(route_name='delete_user', request_method='DELETE')
def delete_user(self):
user = request.matchdict['user_id']
del_user = api.delete_user('username', 'firstname', 'lastname')
return Response{'del_user': user}
TLDR:你有很多问题。也许花更多时间学习 python,然后查看一些关于 Pyramid 和 REST 的博客文章,例如 http://www.vargascarlos.com/2013/02/pyramid-and-rest/ or http://codelike.com/blog/2014/04/27/a-rest-api-with-python-and-pyramid/
您的代码有一些问题,我将首先解决。然后我会提供一些建议,以便更好地安排您的意见。
1) 您在路由配置中使用了 {id}
,因此您将通过 request.matchdict['id']
而不是 request.matchdict['user_id']
.
访问它
额外提示:如果你想强制 ID 只能是数字,你可以使用 {id:\d+}
。冒号后面的部分是正则表达式。这也应该有助于防御 SQL 注入。
2) 您的视图已将自身定义为使用 JSON 渲染器。这意味着您的视图函数应该 return 一个 python 对象(列表、字典等),然后金字塔会将此对象提供给 JSON 渲染器,渲染器将根据需要使用数据,并创建一个响应(在本例中,将其变为有效 json。在模板渲染器的情况下,它将使用模板中的对象)。因此,您不需要 return Response{}
,只需 return {}
。如果您没有定义渲染器,那么您的视图必须自己做出 return 响应。
3) 在你的 class 方法中,比如 get_user
,你需要使用 self.request 访问请求,因为你已经将请求保存到 class 中__init__
.
4) 我认为您是 python 的新手。例如,在 post_user
中,您已使用行 username = request.matchdict['username']
(应该是 username = self.request.params['username']
)将用户名保存到变量用户名中。当您通过 new_user = api.create_user('username', ...
调用 API 时,您传入了一个内容为 'username' 的字符串,因此您创建了一个名为 'username' 的用户。你实际上想传入变量 username
,例如 new_user = api.create_user(username, ...
.
5) 根据#5,python 中的字典是 key:value
。所以在post_user
中,如果你想return键'user'下的json中的新用户,那么你实际上想要return {'user': new_user}
.
所以现在我的主要建议是:你不需要为每个 get/post/etc 路由定义命名路由,你可以定义 1 个路由,然后利用 view predicates 到 运行 正确的函数。
您可能还想创建一个 ConvenienceAPI()
对象,然后共享它(具体取决于该对象的作用及其运作方式)。您甚至可以将其附加到请求中。
您还应该考虑重新使用尽可能多的代码块(即我包含的 get_user
函数)。
在接受了以上所有内容后,您的代码可能如下所示;
config.add_route('users', '/users')
config.add_route('user', '/users/{id:\d+}')
API = ConvenienceAPI()
@view_defaults(route_name='users', renderer='json')
class UsersViews(object):
api = API
def __init__(self, request):
self.request = request
@view_config(request_method='GET')
def get(self):
users = self.api.retrieve_users()
return users
@view_config(request_method='POST')
def post(self):
username = self.request.POST.get('username')
password = self.request.POST.get('password')
firstname = self.request.POST.get('firstname')
lastname = self.request.POST.get('lastname')
email = self.request.POST.get('email')
user = self.api.create_user(username, password, firstname, lastname, email)
return user
@view_defaults(route_name='user', renderer='json')
class UserViews(object):
api = API
def __init__(self, request):
self.request = request
def get_user(self):
user_id = int(self.request.matchdict['id'])
user = self.api.retrieve_user(user_id)
return user
@view_config(request_method='GET')
def get(self):
user = self.get_user()
if user is None:
raise HTTPNotFound()
return user
@view_config(request_method='PUT')
def put(self):
user = self.get_user()
if user is None:
raise HTTPNotFound()
new_username = self.request.POST.get('username')
updated_user = self.api.update_user(user.username, new_username) # This is a strange way of updating the username, but your 'API' is not really relevant to this question.
return HTTPAccepted() # Some REST services return the updated user.
@view_config(request_method='DELETE')
def delete(self):
user = self.get_user()
del_user = self.api.delete_user(user.username, user.firstname, user.lastname)
if del_user: # If user was deleted.
return HTTPAccepted() # Or something like this. As above, you might want to return the deleted user.
else:
return HTTPBadRequest()
这是我第一次尝试设计网络应用程序。我为 SQLAlchemy 数据库创建了一个 CRUD 操作 API。我现在正在尝试使用 Pyramid 设计一个 RESTful 网络框架来支持这个数据库及其所有实体。
我一直在苦苦思索如何PUT(更新)。我知道我还需要加入某些条件(例如代码 202)。我不确定如何使用 PUT 处理对象替换。视图配置中的方法有点混乱,我相信我正在关注数据库 CRUD 操作。
其次,我认为所有资源都必须通过{id}
查找。我相信我在 get_user
方法中是正确的。 User_id
在 db
中保存了用户的所有信息...因此,如果 user
被 id
查找,那么我不需要拥有所有其他信息(参见 delete_user
方法)。
我已经阅读了一些漂亮的 tutorials on designing a RESTful 设计,但考虑到我是自学的,我仍在苦苦挣扎。非常感谢您的帮助!
下面是一些显示问题的代码示例:
_ init _.py (URI):
config.add_route('post_user', '/users') # POST (HTTP) / CREATE (CRUD)
config.add_route('get_user', '/users/{id}') #GET (HTTP) / RETRIEVE (CRUD)
config.add_route('put_user', '/users/{id}') # PUT (HTTP) / UPDATE (CRUD)
config.add_route('delete_user', '/users/{id}') # DELETE (HTTP) / DELETE (CRUD)
views.py(查看配置 -- RESTful):
@view_defaults(renderer='json')
class RESTView(object):
api = ConvenienceAPI() # CRUD API for database
def __init__(self, request):
self.request = request
@view_config(route_name='get_user', request_method='GET')
def get_user(self):
user_id = int(request.matchdict['user_id'])
user = api.retrieve_user('user_id')
if user is None:
raise HTTPNotFound()
else:
return user
@view_config(route_name='post_user', request_method='POST')
def post_user(self):
username = request.matchdict['username']
password = request.matchdict['password']
firstname = request.matchdict['firstname']
lastname = request.matchdict['lastname']
email = request.matchdict['email']
new_user = api.create_user('username', 'password', 'firstname', 'lastname', 'email')
return Response{'new_user': user}
@view_config(route_name='put_user', request_method='PUT')
def put_user(self):
user = request.matchdict['user_id']
new_username = # is this pointing to another URL ????
updated_user = api.update_user('username', 'new_username')
return Response{status='202 Accepted'}
@view_config(route_name='delete_user', request_method='DELETE')
def delete_user(self):
user = request.matchdict['user_id']
del_user = api.delete_user('username', 'firstname', 'lastname')
return Response{'del_user': user}
TLDR:你有很多问题。也许花更多时间学习 python,然后查看一些关于 Pyramid 和 REST 的博客文章,例如 http://www.vargascarlos.com/2013/02/pyramid-and-rest/ or http://codelike.com/blog/2014/04/27/a-rest-api-with-python-and-pyramid/
您的代码有一些问题,我将首先解决。然后我会提供一些建议,以便更好地安排您的意见。
1) 您在路由配置中使用了 {id}
,因此您将通过 request.matchdict['id']
而不是 request.matchdict['user_id']
.
额外提示:如果你想强制 ID 只能是数字,你可以使用 {id:\d+}
。冒号后面的部分是正则表达式。这也应该有助于防御 SQL 注入。
2) 您的视图已将自身定义为使用 JSON 渲染器。这意味着您的视图函数应该 return 一个 python 对象(列表、字典等),然后金字塔会将此对象提供给 JSON 渲染器,渲染器将根据需要使用数据,并创建一个响应(在本例中,将其变为有效 json。在模板渲染器的情况下,它将使用模板中的对象)。因此,您不需要 return Response{}
,只需 return {}
。如果您没有定义渲染器,那么您的视图必须自己做出 return 响应。
3) 在你的 class 方法中,比如 get_user
,你需要使用 self.request 访问请求,因为你已经将请求保存到 class 中__init__
.
4) 我认为您是 python 的新手。例如,在 post_user
中,您已使用行 username = request.matchdict['username']
(应该是 username = self.request.params['username']
)将用户名保存到变量用户名中。当您通过 new_user = api.create_user('username', ...
调用 API 时,您传入了一个内容为 'username' 的字符串,因此您创建了一个名为 'username' 的用户。你实际上想传入变量 username
,例如 new_user = api.create_user(username, ...
.
5) 根据#5,python 中的字典是 key:value
。所以在post_user
中,如果你想return键'user'下的json中的新用户,那么你实际上想要return {'user': new_user}
.
所以现在我的主要建议是:你不需要为每个 get/post/etc 路由定义命名路由,你可以定义 1 个路由,然后利用 view predicates 到 运行 正确的函数。
您可能还想创建一个 ConvenienceAPI()
对象,然后共享它(具体取决于该对象的作用及其运作方式)。您甚至可以将其附加到请求中。
您还应该考虑重新使用尽可能多的代码块(即我包含的 get_user
函数)。
在接受了以上所有内容后,您的代码可能如下所示;
config.add_route('users', '/users')
config.add_route('user', '/users/{id:\d+}')
API = ConvenienceAPI()
@view_defaults(route_name='users', renderer='json')
class UsersViews(object):
api = API
def __init__(self, request):
self.request = request
@view_config(request_method='GET')
def get(self):
users = self.api.retrieve_users()
return users
@view_config(request_method='POST')
def post(self):
username = self.request.POST.get('username')
password = self.request.POST.get('password')
firstname = self.request.POST.get('firstname')
lastname = self.request.POST.get('lastname')
email = self.request.POST.get('email')
user = self.api.create_user(username, password, firstname, lastname, email)
return user
@view_defaults(route_name='user', renderer='json')
class UserViews(object):
api = API
def __init__(self, request):
self.request = request
def get_user(self):
user_id = int(self.request.matchdict['id'])
user = self.api.retrieve_user(user_id)
return user
@view_config(request_method='GET')
def get(self):
user = self.get_user()
if user is None:
raise HTTPNotFound()
return user
@view_config(request_method='PUT')
def put(self):
user = self.get_user()
if user is None:
raise HTTPNotFound()
new_username = self.request.POST.get('username')
updated_user = self.api.update_user(user.username, new_username) # This is a strange way of updating the username, but your 'API' is not really relevant to this question.
return HTTPAccepted() # Some REST services return the updated user.
@view_config(request_method='DELETE')
def delete(self):
user = self.get_user()
del_user = self.api.delete_user(user.username, user.firstname, user.lastname)
if del_user: # If user was deleted.
return HTTPAccepted() # Or something like this. As above, you might want to return the deleted user.
else:
return HTTPBadRequest()