Dodanie omyłkowo pominiętego API.
[wolnelektury.git] / apps / piston / handler.py
diff --git a/apps/piston/handler.py b/apps/piston/handler.py
new file mode 100644 (file)
index 0000000..2d28bb3
--- /dev/null
@@ -0,0 +1,156 @@
+import warnings
+
+from utils import rc
+from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
+from django.conf import settings
+
+typemapper = { }
+handler_tracker = [ ]
+
+class HandlerMetaClass(type):
+    """
+    Metaclass that keeps a registry of class -> handler
+    mappings.
+    """
+    def __new__(cls, name, bases, attrs):
+        new_cls = type.__new__(cls, name, bases, attrs)
+
+        def already_registered(model, anon):
+            for k, (m, a) in typemapper.iteritems():
+                if model == m and anon == a:
+                    return k
+        
+        if hasattr(new_cls, 'model'):
+            if already_registered(new_cls.model, new_cls.is_anonymous):
+                if not getattr(settings, 'PISTON_IGNORE_DUPE_MODELS', False):
+                    warnings.warn("Handler already registered for model %s, "
+                        "you may experience inconsistent results." % new_cls.model.__name__)
+                
+            typemapper[new_cls] = (new_cls.model, new_cls.is_anonymous)
+        else:
+            typemapper[new_cls] = (None, new_cls.is_anonymous)
+        
+        if name not in ('BaseHandler', 'AnonymousBaseHandler'):
+            handler_tracker.append(new_cls)
+
+        return new_cls
+
+class BaseHandler(object):
+    """
+    Basehandler that gives you CRUD for free.
+    You are supposed to subclass this for specific
+    functionality.
+    
+    All CRUD methods (`read`/`update`/`create`/`delete`)
+    receive a request as the first argument from the
+    resource. Use this for checking `request.user`, etc.
+    """
+    __metaclass__ = HandlerMetaClass
+    
+    allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')
+    anonymous = is_anonymous = False
+    exclude = ( 'id', )
+    fields =  ( )
+    
+    def flatten_dict(self, dct):
+        return dict([ (str(k), dct.get(k)) for k in dct.keys() ])
+    
+    def has_model(self):
+        return hasattr(self, 'model') or hasattr(self, 'queryset')
+
+    def queryset(self, request):
+        return self.model.objects.all()
+    
+    def value_from_tuple(tu, name):
+        for int_, n in tu:
+            if n == name:
+                return int_
+        return None
+    
+    def exists(self, **kwargs):
+        if not self.has_model():
+            raise NotImplementedError
+        
+        try:
+            self.model.objects.get(**kwargs)
+            return True
+        except self.model.DoesNotExist:
+            return False
+    
+    def read(self, request, *args, **kwargs):
+        if not self.has_model():
+            return rc.NOT_IMPLEMENTED
+
+        pkfield = self.model._meta.pk.name
+
+        if pkfield in kwargs:
+            try:
+                return self.queryset(request).get(pk=kwargs.get(pkfield))
+            except ObjectDoesNotExist:
+                return rc.NOT_FOUND
+            except MultipleObjectsReturned: # should never happen, since we're using a PK
+                return rc.BAD_REQUEST
+        else:
+            return self.queryset(request).filter(*args, **kwargs)
+    
+    def create(self, request, *args, **kwargs):
+        if not self.has_model():
+            return rc.NOT_IMPLEMENTED
+        
+        attrs = self.flatten_dict(request.POST)
+        
+        try:
+            inst = self.queryset(request).get(**attrs)
+            return rc.DUPLICATE_ENTRY
+        except self.model.DoesNotExist:
+            inst = self.model(**attrs)
+            inst.save()
+            return inst
+        except self.model.MultipleObjectsReturned:
+            return rc.DUPLICATE_ENTRY
+    
+    def update(self, request, *args, **kwargs):
+        if not self.has_model():
+            return rc.NOT_IMPLEMENTED
+
+        pkfield = self.model._meta.pk.name
+
+        if pkfield not in kwargs:
+            # No pk was specified
+            return rc.BAD_REQUEST
+
+        try:
+            inst = self.queryset(request).get(pk=kwargs.get(pkfield))
+        except ObjectDoesNotExist:
+            return rc.NOT_FOUND
+        except MultipleObjectsReturned: # should never happen, since we're using a PK
+            return rc.BAD_REQUEST
+
+        attrs = self.flatten_dict(request.POST)
+        for k,v in attrs.iteritems():
+            setattr( inst, k, v )
+
+        inst.save()
+        return rc.ALL_OK
+    
+    def delete(self, request, *args, **kwargs):
+        if not self.has_model():
+            raise NotImplementedError
+
+        try:
+            inst = self.queryset(request).get(*args, **kwargs)
+
+            inst.delete()
+
+            return rc.DELETED
+        except self.model.MultipleObjectsReturned:
+            return rc.DUPLICATE_ENTRY
+        except self.model.DoesNotExist:
+            return rc.NOT_HERE
+        
+class AnonymousBaseHandler(BaseHandler):
+    """
+    Anonymous handler.
+    """
+    is_anonymous = True
+    allowed_methods = ('GET',)