apipatchrouter.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. from django.core.exceptions import PermissionDenied
  2. from django.db import transaction
  3. from django.http import Http404
  4. from rest_framework.response import Response
  5. ALLOWED_OPS = ('add', 'remove', 'replace')
  6. class InvalidAction(Exception):
  7. pass
  8. class ApiPatchRouter(object):
  9. def __init__(self):
  10. self._actions = []
  11. def add(self, path, handler):
  12. self._actions.append({
  13. 'op': 'add',
  14. 'path': path,
  15. 'handler': handler,
  16. })
  17. def remove(self, path, handler):
  18. self._actions.append({
  19. 'op': 'remove',
  20. 'path': path,
  21. 'handler': handler,
  22. })
  23. def replace(self, path, handler):
  24. self._actions.append({
  25. 'op': 'replace',
  26. 'path': path,
  27. 'handler': handler,
  28. })
  29. def dispatch(self, request, target):
  30. try:
  31. return Response(self.run_actions(request, target))
  32. except Http404:
  33. pass
  34. except PermissionDenied as e:
  35. pass
  36. except InvalidAction as e:
  37. pass
  38. return Response({})
  39. def run_actions(self, request, target):
  40. if not isinstance(request.data, list):
  41. raise InvalidAction("PATCH request should be list of operations")
  42. for action in request.data:
  43. self.validate_action(action)
  44. return self.dispatch_action(request, target, action)
  45. def validate_action(self, action):
  46. if action.get('op') not in ALLOWED_OPS:
  47. if action.get('op'):
  48. raise InvalidAction(
  49. u"\"%s\" op is unsupported" % action.get('op'))
  50. else:
  51. raise InvalidAction(u"server didn't receive valid op")
  52. if not action.get('path'):
  53. raise InvalidAction(
  54. u"\"%s\" op has to define path" % action.get('op'))
  55. if 'value' not in action:
  56. raise InvalidAction(
  57. u"\"%s\" op has to define value" % action.get('op'))
  58. def dispatch_action(self, request, target, action):
  59. patch = {'id': target.pk}
  60. for handler in self._actions:
  61. if (action['op'] == handler['op'] and
  62. action['path'] == handler['path']):
  63. with transaction.atomic():
  64. patch.update(
  65. handler['handler'](request, target, action['value'])
  66. )
  67. return patch