RESTful 金字塔视图配置中的设计

RESTful design in Pyramid View Configuration

这是我第一次尝试设计网络应用程序。我为 SQLAlchemy 数据库创建了一个 CRUD 操作 API。我现在正在尝试使用 Pyramid 设计一个 RESTful 网络框架来支持这个数据库及其所有实体。

我一直在苦苦思索如何PUT(更新)。我知道我还需要加入某些条件(例如代码 202)。我不确定如何使用 PUT 处理对象替换。视图配置中的方法有点混乱,我相信我正在关注数据库 CRUD 操作。

其次,我认为所有资源都必须通过{id}查找。我相信我在 get_user 方法中是正确的。 User_iddb 中保存了用户的所有信息...因此,如果 userid 查找,那么我不需要拥有所有其他信息(参见 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()