Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/django/views/generic/base.py: 35%
133 statements
« prev ^ index » next coverage.py v7.0.5, created at 2023-01-17 06:13 +0000
« prev ^ index » next coverage.py v7.0.5, created at 2023-01-17 06:13 +0000
1import logging
3from asgiref.sync import iscoroutinefunction, markcoroutinefunction
5from django.core.exceptions import ImproperlyConfigured
6from django.http import (
7 HttpResponse,
8 HttpResponseGone,
9 HttpResponseNotAllowed,
10 HttpResponsePermanentRedirect,
11 HttpResponseRedirect,
12)
13from django.template.response import TemplateResponse
14from django.urls import reverse
15from django.utils.decorators import classonlymethod
16from django.utils.functional import classproperty
18logger = logging.getLogger("django.request")
21class ContextMixin:
22 """
23 A default context mixin that passes the keyword arguments received by
24 get_context_data() as the template context.
25 """
27 extra_context = None
29 def get_context_data(self, **kwargs):
30 kwargs.setdefault("view", self)
31 if self.extra_context is not None:
32 kwargs.update(self.extra_context)
33 return kwargs
36class View:
37 """
38 Intentionally simple parent class for all views. Only implements
39 dispatch-by-method and simple sanity checking.
40 """
42 http_method_names = [
43 "get",
44 "post",
45 "put",
46 "patch",
47 "delete",
48 "head",
49 "options",
50 "trace",
51 ]
53 def __init__(self, **kwargs):
54 """
55 Constructor. Called in the URLconf; can contain helpful extra
56 keyword arguments, and other things.
57 """
58 # Go through keyword arguments, and either save their values to our
59 # instance, or raise an error.
60 for key, value in kwargs.items():
61 setattr(self, key, value)
63 @classproperty
64 def view_is_async(cls):
65 handlers = [
66 getattr(cls, method)
67 for method in cls.http_method_names
68 if (method != "options" and hasattr(cls, method))
69 ]
70 if not handlers:
71 return False
72 is_async = iscoroutinefunction(handlers[0])
73 if not all(iscoroutinefunction(h) == is_async for h in handlers[1:]):
74 raise ImproperlyConfigured(
75 f"{cls.__qualname__} HTTP handlers must either be all sync or all "
76 "async."
77 )
78 return is_async
80 @classonlymethod
81 def as_view(cls, **initkwargs):
82 """Main entry point for a request-response process."""
83 for key in initkwargs:
84 if key in cls.http_method_names:
85 raise TypeError(
86 "The method name %s is not accepted as a keyword argument "
87 "to %s()." % (key, cls.__name__)
88 )
89 if not hasattr(cls, key):
90 raise TypeError(
91 "%s() received an invalid keyword %r. as_view "
92 "only accepts arguments that are already "
93 "attributes of the class." % (cls.__name__, key)
94 )
96 def view(request, *args, **kwargs):
97 self = cls(**initkwargs)
98 self.setup(request, *args, **kwargs)
99 if not hasattr(self, "request"):
100 raise AttributeError(
101 "%s instance has no 'request' attribute. Did you override "
102 "setup() and forget to call super()?" % cls.__name__
103 )
104 return self.dispatch(request, *args, **kwargs)
106 view.view_class = cls
107 view.view_initkwargs = initkwargs
109 # __name__ and __qualname__ are intentionally left unchanged as
110 # view_class should be used to robustly determine the name of the view
111 # instead.
112 view.__doc__ = cls.__doc__
113 view.__module__ = cls.__module__
114 view.__annotations__ = cls.dispatch.__annotations__
115 # Copy possible attributes set by decorators, e.g. @csrf_exempt, from
116 # the dispatch method.
117 view.__dict__.update(cls.dispatch.__dict__)
119 # Mark the callback if the view class is async.
120 if cls.view_is_async:
121 markcoroutinefunction(view)
123 return view
125 def setup(self, request, *args, **kwargs):
126 """Initialize attributes shared by all view methods."""
127 if hasattr(self, "get") and not hasattr(self, "head"):
128 self.head = self.get
129 self.request = request
130 self.args = args
131 self.kwargs = kwargs
133 def dispatch(self, request, *args, **kwargs):
134 # Try to dispatch to the right method; if a method doesn't exist,
135 # defer to the error handler. Also defer to the error handler if the
136 # request method isn't on the approved list.
137 if request.method.lower() in self.http_method_names:
138 handler = getattr(
139 self, request.method.lower(), self.http_method_not_allowed
140 )
141 else:
142 handler = self.http_method_not_allowed
143 return handler(request, *args, **kwargs)
145 def http_method_not_allowed(self, request, *args, **kwargs):
146 logger.warning(
147 "Method Not Allowed (%s): %s",
148 request.method,
149 request.path,
150 extra={"status_code": 405, "request": request},
151 )
152 response = HttpResponseNotAllowed(self._allowed_methods())
154 if self.view_is_async:
156 async def func():
157 return response
159 return func()
160 else:
161 return response
163 def options(self, request, *args, **kwargs):
164 """Handle responding to requests for the OPTIONS HTTP verb."""
165 response = HttpResponse()
166 response.headers["Allow"] = ", ".join(self._allowed_methods())
167 response.headers["Content-Length"] = "0"
169 if self.view_is_async:
171 async def func():
172 return response
174 return func()
175 else:
176 return response
178 def _allowed_methods(self):
179 return [m.upper() for m in self.http_method_names if hasattr(self, m)]
182class TemplateResponseMixin:
183 """A mixin that can be used to render a template."""
185 template_name = None
186 template_engine = None
187 response_class = TemplateResponse
188 content_type = None
190 def render_to_response(self, context, **response_kwargs):
191 """
192 Return a response, using the `response_class` for this view, with a
193 template rendered with the given context.
195 Pass response_kwargs to the constructor of the response class.
196 """
197 response_kwargs.setdefault("content_type", self.content_type)
198 return self.response_class(
199 request=self.request,
200 template=self.get_template_names(),
201 context=context,
202 using=self.template_engine,
203 **response_kwargs,
204 )
206 def get_template_names(self):
207 """
208 Return a list of template names to be used for the request. Must return
209 a list. May not be called if render_to_response() is overridden.
210 """
211 if self.template_name is None:
212 raise ImproperlyConfigured(
213 "TemplateResponseMixin requires either a definition of "
214 "'template_name' or an implementation of 'get_template_names()'"
215 )
216 else:
217 return [self.template_name]
220class TemplateView(TemplateResponseMixin, ContextMixin, View):
221 """
222 Render a template. Pass keyword arguments from the URLconf to the context.
223 """
225 def get(self, request, *args, **kwargs):
226 context = self.get_context_data(**kwargs)
227 return self.render_to_response(context)
230class RedirectView(View):
231 """Provide a redirect on any GET request."""
233 permanent = False
234 url = None
235 pattern_name = None
236 query_string = False
238 def get_redirect_url(self, *args, **kwargs):
239 """
240 Return the URL redirect to. Keyword arguments from the URL pattern
241 match generating the redirect request are provided as kwargs to this
242 method.
243 """
244 if self.url:
245 url = self.url % kwargs
246 elif self.pattern_name:
247 url = reverse(self.pattern_name, args=args, kwargs=kwargs)
248 else:
249 return None
251 args = self.request.META.get("QUERY_STRING", "")
252 if args and self.query_string:
253 url = "%s?%s" % (url, args)
254 return url
256 def get(self, request, *args, **kwargs):
257 url = self.get_redirect_url(*args, **kwargs)
258 if url:
259 if self.permanent:
260 return HttpResponsePermanentRedirect(url)
261 else:
262 return HttpResponseRedirect(url)
263 else:
264 logger.warning(
265 "Gone: %s", request.path, extra={"status_code": 410, "request": request}
266 )
267 return HttpResponseGone()
269 def head(self, request, *args, **kwargs):
270 return self.get(request, *args, **kwargs)
272 def post(self, request, *args, **kwargs):
273 return self.get(request, *args, **kwargs)
275 def options(self, request, *args, **kwargs):
276 return self.get(request, *args, **kwargs)
278 def delete(self, request, *args, **kwargs):
279 return self.get(request, *args, **kwargs)
281 def put(self, request, *args, **kwargs):
282 return self.get(request, *args, **kwargs)
284 def patch(self, request, *args, **kwargs):
285 return self.get(request, *args, **kwargs)