New design for book-detail template (downloading books etc).
[wolnelektury.git] / apps / piston / handler.py
1 import warnings
2
3 from utils import rc
4 from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
5 from django.conf import settings
6
7 typemapper = { }
8 handler_tracker = [ ]
9
10 class HandlerMetaClass(type):
11     """
12     Metaclass that keeps a registry of class -> handler
13     mappings.
14     """
15     def __new__(cls, name, bases, attrs):
16         new_cls = type.__new__(cls, name, bases, attrs)
17
18         def already_registered(model, anon):
19             for k, (m, a) in typemapper.iteritems():
20                 if model == m and anon == a:
21                     return k
22
23         if hasattr(new_cls, 'model'):
24             if already_registered(new_cls.model, new_cls.is_anonymous):
25                 if not getattr(settings, 'PISTON_IGNORE_DUPE_MODELS', False):
26                     warnings.warn("Handler already registered for model %s, "
27                         "you may experience inconsistent results." % new_cls.model.__name__)
28
29             typemapper[new_cls] = (new_cls.model, new_cls.is_anonymous)
30         else:
31             typemapper[new_cls] = (None, new_cls.is_anonymous)
32
33         if name not in ('BaseHandler', 'AnonymousBaseHandler'):
34             handler_tracker.append(new_cls)
35
36         return new_cls
37
38 class BaseHandler(object):
39     """
40     Basehandler that gives you CRUD for free.
41     You are supposed to subclass this for specific
42     functionality.
43
44     All CRUD methods (`read`/`update`/`create`/`delete`)
45     receive a request as the first argument from the
46     resource. Use this for checking `request.user`, etc.
47     """
48     __metaclass__ = HandlerMetaClass
49
50     allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')
51     anonymous = is_anonymous = False
52     exclude = ( 'id', )
53     fields =  ( )
54
55     def flatten_dict(self, dct):
56         return dict([ (str(k), dct.get(k)) for k in dct.keys() ])
57
58     def has_model(self):
59         return hasattr(self, 'model') or hasattr(self, 'queryset')
60
61     def queryset(self, request):
62         return self.model.objects.all()
63
64     def value_from_tuple(tu, name):
65         for int_, n in tu:
66             if n == name:
67                 return int_
68         return None
69
70     def exists(self, **kwargs):
71         if not self.has_model():
72             raise NotImplementedError
73
74         try:
75             self.model.objects.get(**kwargs)
76             return True
77         except self.model.DoesNotExist:
78             return False
79
80     def read(self, request, *args, **kwargs):
81         if not self.has_model():
82             return rc.NOT_IMPLEMENTED
83
84         pkfield = self.model._meta.pk.name
85
86         if pkfield in kwargs:
87             try:
88                 return self.queryset(request).get(pk=kwargs.get(pkfield))
89             except ObjectDoesNotExist:
90                 return rc.NOT_FOUND
91             except MultipleObjectsReturned: # should never happen, since we're using a PK
92                 return rc.BAD_REQUEST
93         else:
94             return self.queryset(request).filter(*args, **kwargs)
95
96     def create(self, request, *args, **kwargs):
97         if not self.has_model():
98             return rc.NOT_IMPLEMENTED
99
100         attrs = self.flatten_dict(request.POST)
101
102         try:
103             inst = self.queryset(request).get(**attrs)
104             return rc.DUPLICATE_ENTRY
105         except self.model.DoesNotExist:
106             inst = self.model(**attrs)
107             inst.save()
108             return inst
109         except self.model.MultipleObjectsReturned:
110             return rc.DUPLICATE_ENTRY
111
112     def update(self, request, *args, **kwargs):
113         if not self.has_model():
114             return rc.NOT_IMPLEMENTED
115
116         pkfield = self.model._meta.pk.name
117
118         if pkfield not in kwargs:
119             # No pk was specified
120             return rc.BAD_REQUEST
121
122         try:
123             inst = self.queryset(request).get(pk=kwargs.get(pkfield))
124         except ObjectDoesNotExist:
125             return rc.NOT_FOUND
126         except MultipleObjectsReturned: # should never happen, since we're using a PK
127             return rc.BAD_REQUEST
128
129         attrs = self.flatten_dict(request.POST)
130         for k,v in attrs.iteritems():
131             setattr( inst, k, v )
132
133         inst.save()
134         return rc.ALL_OK
135
136     def delete(self, request, *args, **kwargs):
137         if not self.has_model():
138             raise NotImplementedError
139
140         try:
141             inst = self.queryset(request).get(*args, **kwargs)
142
143             inst.delete()
144
145             return rc.DELETED
146         except self.model.MultipleObjectsReturned:
147             return rc.DUPLICATE_ENTRY
148         except self.model.DoesNotExist:
149             return rc.NOT_HERE
150
151 class AnonymousBaseHandler(BaseHandler):
152     """
153     Anonymous handler.
154     """
155     is_anonymous = True
156     allowed_methods = ('GET',)