Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

base.py 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import logging
  2. from functools import update_wrapper
  3. from django.core.exceptions import ImproperlyConfigured
  4. from django.http import (
  5. HttpResponse, HttpResponseGone, HttpResponseNotAllowed,
  6. HttpResponsePermanentRedirect, HttpResponseRedirect,
  7. )
  8. from django.template.response import TemplateResponse
  9. from django.urls import reverse
  10. from django.utils.decorators import classonlymethod
  11. logger = logging.getLogger('django.request')
  12. class ContextMixin:
  13. """
  14. A default context mixin that passes the keyword arguments received by
  15. get_context_data() as the template context.
  16. """
  17. extra_context = None
  18. def get_context_data(self, **kwargs):
  19. kwargs.setdefault('view', self)
  20. if self.extra_context is not None:
  21. kwargs.update(self.extra_context)
  22. return kwargs
  23. class View:
  24. """
  25. Intentionally simple parent class for all views. Only implements
  26. dispatch-by-method and simple sanity checking.
  27. """
  28. http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
  29. def __init__(self, **kwargs):
  30. """
  31. Constructor. Called in the URLconf; can contain helpful extra
  32. keyword arguments, and other things.
  33. """
  34. # Go through keyword arguments, and either save their values to our
  35. # instance, or raise an error.
  36. for key, value in kwargs.items():
  37. setattr(self, key, value)
  38. @classonlymethod
  39. def as_view(cls, **initkwargs):
  40. """Main entry point for a request-response process."""
  41. for key in initkwargs:
  42. if key in cls.http_method_names:
  43. raise TypeError("You tried to pass in the %s method name as a "
  44. "keyword argument to %s(). Don't do that."
  45. % (key, cls.__name__))
  46. if not hasattr(cls, key):
  47. raise TypeError("%s() received an invalid keyword %r. as_view "
  48. "only accepts arguments that are already "
  49. "attributes of the class." % (cls.__name__, key))
  50. def view(request, *args, **kwargs):
  51. self = cls(**initkwargs)
  52. if hasattr(self, 'get') and not hasattr(self, 'head'):
  53. self.head = self.get
  54. self.request = request
  55. self.args = args
  56. self.kwargs = kwargs
  57. return self.dispatch(request, *args, **kwargs)
  58. view.view_class = cls
  59. view.view_initkwargs = initkwargs
  60. # take name and docstring from class
  61. update_wrapper(view, cls, updated=())
  62. # and possible attributes set by decorators
  63. # like csrf_exempt from dispatch
  64. update_wrapper(view, cls.dispatch, assigned=())
  65. return view
  66. def dispatch(self, request, *args, **kwargs):
  67. # Try to dispatch to the right method; if a method doesn't exist,
  68. # defer to the error handler. Also defer to the error handler if the
  69. # request method isn't on the approved list.
  70. if request.method.lower() in self.http_method_names:
  71. handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
  72. else:
  73. handler = self.http_method_not_allowed
  74. return handler(request, *args, **kwargs)
  75. def http_method_not_allowed(self, request, *args, **kwargs):
  76. logger.warning(
  77. 'Method Not Allowed (%s): %s', request.method, request.path,
  78. extra={'status_code': 405, 'request': request}
  79. )
  80. return HttpResponseNotAllowed(self._allowed_methods())
  81. def options(self, request, *args, **kwargs):
  82. """Handle responding to requests for the OPTIONS HTTP verb."""
  83. response = HttpResponse()
  84. response['Allow'] = ', '.join(self._allowed_methods())
  85. response['Content-Length'] = '0'
  86. return response
  87. def _allowed_methods(self):
  88. return [m.upper() for m in self.http_method_names if hasattr(self, m)]
  89. class TemplateResponseMixin:
  90. """A mixin that can be used to render a template."""
  91. template_name = None
  92. template_engine = None
  93. response_class = TemplateResponse
  94. content_type = None
  95. def render_to_response(self, context, **response_kwargs):
  96. """
  97. Return a response, using the `response_class` for this view, with a
  98. template rendered with the given context.
  99. Pass response_kwargs to the constructor of the response class.
  100. """
  101. response_kwargs.setdefault('content_type', self.content_type)
  102. return self.response_class(
  103. request=self.request,
  104. template=self.get_template_names(),
  105. context=context,
  106. using=self.template_engine,
  107. **response_kwargs
  108. )
  109. def get_template_names(self):
  110. """
  111. Return a list of template names to be used for the request. Must return
  112. a list. May not be called if render_to_response() is overridden.
  113. """
  114. if self.template_name is None:
  115. raise ImproperlyConfigured(
  116. "TemplateResponseMixin requires either a definition of "
  117. "'template_name' or an implementation of 'get_template_names()'")
  118. else:
  119. return [self.template_name]
  120. class TemplateView(TemplateResponseMixin, ContextMixin, View):
  121. """
  122. Render a template. Pass keyword arguments from the URLconf to the context.
  123. """
  124. def get(self, request, *args, **kwargs):
  125. context = self.get_context_data(**kwargs)
  126. return self.render_to_response(context)
  127. class RedirectView(View):
  128. """Provide a redirect on any GET request."""
  129. permanent = False
  130. url = None
  131. pattern_name = None
  132. query_string = False
  133. def get_redirect_url(self, *args, **kwargs):
  134. """
  135. Return the URL redirect to. Keyword arguments from the URL pattern
  136. match generating the redirect request are provided as kwargs to this
  137. method.
  138. """
  139. if self.url:
  140. url = self.url % kwargs
  141. elif self.pattern_name:
  142. url = reverse(self.pattern_name, args=args, kwargs=kwargs)
  143. else:
  144. return None
  145. args = self.request.META.get('QUERY_STRING', '')
  146. if args and self.query_string:
  147. url = "%s?%s" % (url, args)
  148. return url
  149. def get(self, request, *args, **kwargs):
  150. url = self.get_redirect_url(*args, **kwargs)
  151. if url:
  152. if self.permanent:
  153. return HttpResponsePermanentRedirect(url)
  154. else:
  155. return HttpResponseRedirect(url)
  156. else:
  157. logger.warning(
  158. 'Gone: %s', request.path,
  159. extra={'status_code': 410, 'request': request}
  160. )
  161. return HttpResponseGone()
  162. def head(self, request, *args, **kwargs):
  163. return self.get(request, *args, **kwargs)
  164. def post(self, request, *args, **kwargs):
  165. return self.get(request, *args, **kwargs)
  166. def options(self, request, *args, **kwargs):
  167. return self.get(request, *args, **kwargs)
  168. def delete(self, request, *args, **kwargs):
  169. return self.get(request, *args, **kwargs)
  170. def put(self, request, *args, **kwargs):
  171. return self.get(request, *args, **kwargs)
  172. def patch(self, request, *args, **kwargs):
  173. return self.get(request, *args, **kwargs)