aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework/resources.py
blob: dd8a547166a01bd491440b4d9fcb5d95daab2e1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
##### RESOURCES AND ROUTERS ARE NOT YET IMPLEMENTED - PLACEHOLDER ONLY #####

from functools import update_wrapper
import inspect
from django.utils.decorators import classonlymethod
from rest_framework import views, generics


def wrapped(source, dest):
    """
    Copy public, non-method attributes from source to dest, and return dest.
    """
    for attr in [attr for attr in dir(source)
                 if not attr.startswith('_') and not inspect.ismethod(attr)]:
        setattr(dest, attr, getattr(source, attr))
    return dest


##### RESOURCES AND ROUTERS ARE NOT YET IMPLEMENTED - PLACEHOLDER ONLY #####

class ResourceMixin(object):
    """
    Clone Django's `View.as_view()` behaviour *except* using REST framework's
    'method -> action' binding for resources.
    """

    @classonlymethod
    def as_view(cls, actions, **initkwargs):
        """
        Main entry point for a request-response process.
        """
        # 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
            for method, action in actions.items():
                handler = getattr(self, action)
                setattr(self, method, handler)

            # As you were, solider.
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            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


##### RESOURCES AND ROUTERS ARE NOT YET IMPLEMENTED - PLACEHOLDER ONLY #####

class Resource(ResourceMixin, views.APIView):
    pass


##### RESOURCES AND ROUTERS ARE NOT YET IMPLEMENTED - PLACEHOLDER ONLY #####

class ModelResource(ResourceMixin, views.APIView):
    # TODO: Actually delegation won't work
    root_class = generics.ListCreateAPIView
    detail_class = generics.RetrieveUpdateDestroyAPIView

    def root_view(self):
        return wrapped(self, self.root_class())

    def detail_view(self):
        return wrapped(self, self.detail_class())

    def list(self, request, *args, **kwargs):
        return self.root_view().list(request, args, kwargs)

    def create(self, request, *args, **kwargs):
        return self.root_view().create(request, args, kwargs)

    def retrieve(self, request, *args, **kwargs):
        return self.detail_view().retrieve(request, args, kwargs)

    def update(self, request, *args, **kwargs):
        return self.detail_view().update(request, args, kwargs)

    def destroy(self, request, *args, **kwargs):
        return self.detail_view().destroy(request, args, kwargs)