From ec076a00786c6b89a55b6ffe2556bb3b777100f5 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 31 Mar 2013 11:36:58 +0100 Subject: Add viewsets/routers to indexs etc --- rest_framework/viewsets.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 rest_framework/viewsets.py (limited to 'rest_framework/viewsets.py') diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py new file mode 100644 index 00000000..a5aef5b7 --- /dev/null +++ b/rest_framework/viewsets.py @@ -0,0 +1,33 @@ +# Not properly implemented yet, just the basic idea + + +class BaseRouter(object): + def __init__(self): + self.resources = [] + + def register(self, name, resource): + self.resources.append((name, resource)) + + @property + def urlpatterns(self): + ret = [] + + for name, resource in self.resources: + list_actions = { + 'get': getattr(resource, 'list', None), + 'post': getattr(resource, 'create', None) + } + detail_actions = { + 'get': getattr(resource, 'retrieve', None), + 'put': getattr(resource, 'update', None), + 'delete': getattr(resource, 'destroy', None) + } + list_regex = r'^%s/$' % name + detail_regex = r'^%s/(?P[0-9]+)/$' % name + list_name = '%s-list' + detail_name = '%s-detail' + + ret += url(list_regex, resource.as_view(list_actions), list_name) + ret += url(detail_regex, resource.as_view(detail_actions), detail_name) + + return ret -- cgit v1.2.3 From c785628300d2b7cce63862a18915c537f8a3ab24 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 4 Apr 2013 20:00:44 +0100 Subject: Fleshing out viewsets/routers --- rest_framework/viewsets.py | 119 ++++++++++++++++++++++++++++++++------------- 1 file changed, 86 insertions(+), 33 deletions(-) (limited to 'rest_framework/viewsets.py') diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py index a5aef5b7..887a9722 100644 --- a/rest_framework/viewsets.py +++ b/rest_framework/viewsets.py @@ -1,33 +1,86 @@ -# Not properly implemented yet, just the basic idea - - -class BaseRouter(object): - def __init__(self): - self.resources = [] - - def register(self, name, resource): - self.resources.append((name, resource)) - - @property - def urlpatterns(self): - ret = [] - - for name, resource in self.resources: - list_actions = { - 'get': getattr(resource, 'list', None), - 'post': getattr(resource, 'create', None) - } - detail_actions = { - 'get': getattr(resource, 'retrieve', None), - 'put': getattr(resource, 'update', None), - 'delete': getattr(resource, 'destroy', None) - } - list_regex = r'^%s/$' % name - detail_regex = r'^%s/(?P[0-9]+)/$' % name - list_name = '%s-list' - detail_name = '%s-detail' - - ret += url(list_regex, resource.as_view(list_actions), list_name) - ret += url(detail_regex, resource.as_view(detail_actions), detail_name) - - return ret +from functools import update_wrapper +from django.utils.decorators import classonlymethod +from rest_framework import views, generics, mixins + + +class ViewSetMixin(object): + """ + This is the magic. + + Overrides `.as_view()` so that it takes an `actions` keyword that performs + the binding of HTTP methods to actions on the Resource. + + For example, to create a concrete view binding the 'GET' and 'POST' methods + to the 'list' and 'create' actions... + + view = MyViewSet.as_view({'get': 'list', 'post': 'create'}) + """ + + @classonlymethod + def as_view(cls, actions=None, **initkwargs): + """ + Main entry point for a request-response process. + + Because of the way class based views create a closure around the + instantiated view, we need to totally reimplement `.as_view`, + and slightly modify the view function that is created and returned. + """ + # sanitize keyword arguments + for key in initkwargs: + if key in cls.http_method_names: + raise TypeError("You tried to pass in the %s method name as a " + "keyword argument to %s(). Don't do that." + % (key, cls.__name__)) + if not hasattr(cls, key): + raise TypeError("%s() received an invalid keyword %r" % ( + cls.__name__, key)) + + def view(request, *args, **kwargs): + self = cls(**initkwargs) + + # Bind methods to actions + # This is the bit that's different to a standard view + for method, action in actions.items(): + handler = getattr(self, action) + setattr(self, method, handler) + + # Patch this in as it's otherwise only present from 1.5 onwards + if hasattr(self, 'get') and not hasattr(self, 'head'): + self.head = self.get + + # And continue as usual + return self.dispatch(request, *args, **kwargs) + + # take name and docstring from class + update_wrapper(view, cls, updated=()) + + # and possible attributes set by decorators + # like csrf_exempt from dispatch + update_wrapper(view, cls.dispatch, assigned=()) + return view + + +class ViewSet(ViewSetMixin, views.APIView): + pass + + +# Note the inheritence of both MultipleObjectAPIView *and* SingleObjectAPIView +# is a bit weird given the diamond inheritence, but it will work for now. +# There's some implementation clean up that can happen later. +class ModelViewSet(mixins.CreateModelMixin, + mixins.RetrieveModelMixin, + mixins.UpdateModelMixin, + mixins.DestroyModelMixin, + mixins.ListModelMixin, + ViewSetMixin, + generics.MultipleObjectAPIView, + generics.SingleObjectAPIView): + pass + + +class ReadOnlyModelViewSet(mixins.RetrieveModelMixin, + mixins.ListModelMixin, + ViewSetMixin, + generics.MultipleObjectAPIView, + generics.SingleObjectAPIView): + pass -- cgit v1.2.3 From f68721ade8d66806296323116ff9a61773ad2be1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 4 Apr 2013 21:42:26 +0100 Subject: Factor view names/descriptions out of View class --- rest_framework/viewsets.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'rest_framework/viewsets.py') diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py index 887a9722..0818c0d9 100644 --- a/rest_framework/viewsets.py +++ b/rest_framework/viewsets.py @@ -15,9 +15,10 @@ class ViewSetMixin(object): view = MyViewSet.as_view({'get': 'list', 'post': 'create'}) """ + _is_viewset = True @classonlymethod - def as_view(cls, actions=None, **initkwargs): + def as_view(cls, actions=None, name_suffix=None, **initkwargs): """ Main entry point for a request-response process. @@ -57,6 +58,8 @@ class ViewSetMixin(object): # and possible attributes set by decorators # like csrf_exempt from dispatch update_wrapper(view, cls.dispatch, assigned=()) + + view.cls = cls return view -- cgit v1.2.3 From 9bb1277e512a88e6c11c52457d0c24e73f30bb98 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 9 Apr 2013 19:37:19 +0100 Subject: Cleaning up around bits of API that will be pending deprecation --- rest_framework/viewsets.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'rest_framework/viewsets.py') diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py index 0818c0d9..28ab30e2 100644 --- a/rest_framework/viewsets.py +++ b/rest_framework/viewsets.py @@ -76,14 +76,12 @@ class ModelViewSet(mixins.CreateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, ViewSetMixin, - generics.MultipleObjectAPIView, - generics.SingleObjectAPIView): + generics.GenericAPIView): pass class ReadOnlyModelViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, ViewSetMixin, - generics.MultipleObjectAPIView, - generics.SingleObjectAPIView): + generics.GenericAPIView): pass -- cgit v1.2.3