X-Git-Url: https://git.mdrn.pl/wolnelektury.git/blobdiff_plain/d0ab1d8908cadac9f51a17e2fe9e1193f14e28cc..0e87ae0739ed3e72301b7b718098f97a7f06a5d8:/apps/piston/handler.py diff --git a/apps/piston/handler.py b/apps/piston/handler.py new file mode 100644 index 000000000..2d28bb3d5 --- /dev/null +++ b/apps/piston/handler.py @@ -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',)