简体   繁体   English

从tastypie上的资源中删除列表端点

[英]Removing the list endpoint from a resource on tastypie

I have a Resource on my api that always return the logged-in user. 我的api上有一个Resource,它总是返回登录用户。 The resource is read-only. 资源是只读的。 I wanted the list uri to act as the detail uri, and remove the detail urls. 我希望列表uri充当详细信息uri,并删除详细信息URL。

So, /api/v1/user/ would return the logged user, and any other url would fail. 因此, /api/v1/user/将返回已记录的用户,并且任何其他URL将失败。 This is what I did to achieve this: 这就是我为实现这一点所做的:

class UserResource(ModelResource):
    class Meta:
        queryset = User.objects.all()
        fields = ['email', 'name']
        authentication = MultiAuthentication(SessionAuthentication(), BasicAuthentication())
        authorization = Authorization()
        list_allowed_methods = []
        detail_allowed_methods = ['get']

    def base_urls(self):
        '''
        The list endpoint behaves as the list endpoint.
        '''
        return [
            url(r"^(?P<resource_name>%s)%s$" % (self._meta.resource_name, trailing_slash()), self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
            url(r"^(?P<resource_name>%s)/schema%s$" % (self._meta.resource_name, trailing_slash()), self.wrap_view('get_schema'), name="api_get_schema")
        ]

    def obj_get(self, bundle, **kwargs):
        '''
        Always returns the logged in user.
        '''
        return bundle.request.user

    def get_resource_uri(self, bundle_or_obj=None, url_name='api_dispatch_detail'):
        bundle_or_obj = None
        try:
            return self._build_reverse_url(url_name, kwargs=self.resource_uri_kwargs(bundle_or_obj))
        except NoReverseMatch:
            return ''

I used base_urls() instead of prepend_urls() because I wanted to remove the other urls. 我使用base_urls()而不是prepend_urls()因为我想删除其他网址。

It works fine, but when I hit the /api/v1/ url, I get this error: 它运行正常,但是当我点击/api/v1/ url时,我收到此错误:

Traceback:
File "/home/vagrant/workspace/expenses/venv/local/lib/python2.7/site-packages/Django-1.5-py2.7.egg/django/core/handlers/base.py" in get_response
  115.                         response = callback(request, *callback_args, **callback_kwargs)
File "/home/vagrant/workspace/expenses/venv/local/lib/python2.7/site-packages/django_tastypie-0.9.15-py2.7.egg/tastypie/api.py" in wrapper
  80.                 return getattr(self, view)(request, *args, **kwargs)
File "/home/vagrant/workspace/expenses/venv/local/lib/python2.7/site-packages/django_tastypie-0.9.15-py2.7.egg/tastypie/api.py" in top_level
  137.                     'resource_name': name,
File "/home/vagrant/workspace/expenses/venv/local/lib/python2.7/site-packages/django_tastypie-0.9.15-py2.7.egg/tastypie/api.py" in _build_reverse_url
  166.         return reverse(name, args=args, kwargs=kwargs)
File "/home/vagrant/workspace/expenses/venv/local/lib/python2.7/site-packages/Django-1.5-py2.7.egg/django/core/urlresolvers.py" in reverse
  496.     return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs))
File "/home/vagrant/workspace/expenses/venv/local/lib/python2.7/site-packages/Django-1.5-py2.7.egg/django/core/urlresolvers.py" in _reverse_with_prefix
  416.                 "arguments '%s' not found." % (lookup_view_s, args, kwargs))

Exception Type: NoReverseMatch at /api/v1/
Exception Value: Reverse for 'api_dispatch_list' with arguments '()' and keyword arguments '{'api_name': u'v1', 'resource_name': 'user'}' not found.

It's trying to reach the missing list endpoint. 它正试图到达缺失列表端点。 How to I get rid of this? 如何摆脱这个?

Thanks. 谢谢。


Thanks to Rudy's guidance, I ended up with the following: 感谢Rudy的指导,我最终得到了以下结论:

class UserResource(ModelResource):
    class Meta:
        queryset = User.objects.all()
        fields = ['email', 'name']
        authentication = MultiAuthentication(SessionAuthentication(), BasicAuthentication())
        authorization = Authorization()
        list_allowed_methods = []
        detail_allowed_methods = ['get']

    def dispatch_list(self, request, **kwargs):
        return self.dispatch_detail(request, **kwargs)

    def obj_get(self, bundle, **kwargs):
        '''
        Always returns the logged in user.
        '''
        return bundle.request.user

    def get_resource_uri(self, bundle_or_obj=None, url_name='api_dispatch_list'):
        bundle_or_obj = None
        try:
            return self._build_reverse_url(url_name, kwargs=self.resource_uri_kwargs(bundle_or_obj))
        except NoReverseMatch:
            return ''

You should use a custom Authorization class that blocks the list endpoints and gracefully raises an error instead of just removing the URL all together so it still plays nicely with Tastypie. 您应该使用一个自定义授权类来阻止列表端点,并优雅地引发错误,而不是仅仅删除所有URL,这样它仍然可以很好地与Tastypie一起使用。

class UserObjectsOnlyAuthorization(Authorization):
def read_list(self, object_list, bundle):
    raise Unauthorized("Sorry, no list reads.")

def read_detail(self, object_list, bundle):
    # Is the requested object the user?
    return bundle.obj == bundle.request.user

def create_list(self, object_list, bundle):
    raise Unauthorized("Sorry, no creates.")

def create_detail(self, object_list, bundle):
    raise Unauthorized("Sorry, no creates.")

def update_list(self, object_list, bundle):
    raise Unauthorized("Sorry, no updates.")

def update_detail(self, object_list, bundle):
    raise Unauthorized("Sorry, no updates.")

def delete_list(self, object_list, bundle):
    # Sorry user, no deletes for you!
    raise Unauthorized("Sorry, no deletes.")

def delete_detail(self, object_list, bundle):
    raise Unauthorized("Sorry, no deletes.")

EDIT: 编辑:

If you'd like to force this API always to be a 'Detail' request then you can override Tastypie's built in functions. 如果您想强制此API始终为“详细信息”请求,则可以覆盖Tastypie的内置函数。 Basically if you specify an ID in the URL then tastypie routes it to be a _detail request and if you don't then it routes it to be a _list request. 基本上,如果您在URL中指定了ID,则tastypie将其路由为_detail请求,如果不指定,则将其路由为_list请求。 If you override the dispatch functions which detect this, you can change all requests to this resource to be _detail and specify what the primary key is to look up your user. 如果覆盖检测到此功能的调度功能,则可以将对此资源的所有请求更改为_detail,并指定主键用于查找用户的内容。 This may be a bit more hacky, but will accomplish what you want: 这可能会更加hacky,但会完成你想要的:

def dispatch(self, request_type, request, **kwargs):
    # Force this to be a single User object
    return super(UserResource, self).dispatch('detail', request, **kwargs)

def get_detail(self, request, **kwargs):
    # Place the authenticated user's id in the get detail request
    kwargs['id'] = request.user.pk
    return super(UserResource, self).get_detail(request, **kwargs)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM