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

时间:2015-09-16 17:36:19

标签: python rest uri pyramid crud

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

我一直在努力研究如何进行PUT(更新)。我知道我还需要纳入某些条件(例如Code 202)。我不确定如何使用PUT处理对象替换。视图配置中的方法有点令人困惑,我相信我正在遵循db CRUD操作。

其次,我认为必须通过{id}查找所有资源。我相信我在get_user方法中有这个正确的。 User_iddb中保存用户的所有信息...因此,如果user查找了id,那么我不需要全部其他信息(见delete_user方法)。

我在设计tutorials设计时已经阅读了一些漂亮的RESTful,但鉴于我是自学成才,我仍在苦苦挣扎。真的很感谢您的帮助!

以下是一些显示问题的代码示例:

_ init _.py(URIs):

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}

1 个答案:

答案 0 :(得分:7)

TLDR:你有很多问题。也许花更多时间学习python,然后查看金字塔和REST上的一些博客文章,如http://www.vargascarlos.com/2013/02/pyramid-and-rest/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渲染器。这意味着你的视图函数应该返回一个python对象(list,dict等),然后金字塔会将这个对象提供给JSON渲染器,它会随意使用数据并创建一个响应(在这种情况下,转换它)进入有效的json。对于模板渲染器,它将使用模板中的对象)。因此,您不需要return Response{},而只需return {}。如果您没有定义渲染器,那么您的视图必须自己制作并返回响应。

3)在get_user等类方法中,您需要使用self.request访问请求,因为您已将请求保存到__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中的dicts是key:value。因此,在post_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()