Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/flask/views.py: 54%
48 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:03 +0000
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:03 +0000
1import typing as t
3from . import typing as ft
4from .globals import current_app
5from .globals import request
8http_method_funcs = frozenset(
9 ["get", "post", "head", "options", "delete", "put", "trace", "patch"]
10)
13class View:
14 """Alternative way to use view functions. A subclass has to implement
15 :meth:`dispatch_request` which is called with the view arguments from
16 the URL routing system. If :attr:`methods` is provided the methods
17 do not have to be passed to the :meth:`~flask.Flask.add_url_rule`
18 method explicitly::
20 class MyView(View):
21 methods = ['GET']
23 def dispatch_request(self, name):
24 return f"Hello {name}!"
26 app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview'))
28 When you want to decorate a pluggable view you will have to either do that
29 when the view function is created (by wrapping the return value of
30 :meth:`as_view`) or you can use the :attr:`decorators` attribute::
32 class SecretView(View):
33 methods = ['GET']
34 decorators = [superuser_required]
36 def dispatch_request(self):
37 ...
39 The decorators stored in the decorators list are applied one after another
40 when the view function is created. Note that you can *not* use the class
41 based decorators since those would decorate the view class and not the
42 generated view function!
43 """
45 #: A list of methods this view can handle.
46 methods: t.Optional[t.List[str]] = None
48 #: Setting this disables or force-enables the automatic options handling.
49 provide_automatic_options: t.Optional[bool] = None
51 #: The canonical way to decorate class-based views is to decorate the
52 #: return value of as_view(). However since this moves parts of the
53 #: logic from the class declaration to the place where it's hooked
54 #: into the routing system.
55 #:
56 #: You can place one or more decorators in this list and whenever the
57 #: view function is created the result is automatically decorated.
58 #:
59 #: .. versionadded:: 0.8
60 decorators: t.List[t.Callable] = []
62 def dispatch_request(self) -> ft.ResponseReturnValue:
63 """Subclasses have to override this method to implement the
64 actual view function code. This method is called with all
65 the arguments from the URL rule.
66 """
67 raise NotImplementedError()
69 @classmethod
70 def as_view(
71 cls, name: str, *class_args: t.Any, **class_kwargs: t.Any
72 ) -> t.Callable:
73 """Converts the class into an actual view function that can be used
74 with the routing system. Internally this generates a function on the
75 fly which will instantiate the :class:`View` on each request and call
76 the :meth:`dispatch_request` method on it.
78 The arguments passed to :meth:`as_view` are forwarded to the
79 constructor of the class.
80 """
82 def view(*args: t.Any, **kwargs: t.Any) -> ft.ResponseReturnValue:
83 self = view.view_class(*class_args, **class_kwargs) # type: ignore
84 return current_app.ensure_sync(self.dispatch_request)(*args, **kwargs)
86 if cls.decorators:
87 view.__name__ = name
88 view.__module__ = cls.__module__
89 for decorator in cls.decorators:
90 view = decorator(view)
92 # We attach the view class to the view function for two reasons:
93 # first of all it allows us to easily figure out what class-based
94 # view this thing came from, secondly it's also used for instantiating
95 # the view class so you can actually replace it with something else
96 # for testing purposes and debugging.
97 view.view_class = cls # type: ignore
98 view.__name__ = name
99 view.__doc__ = cls.__doc__
100 view.__module__ = cls.__module__
101 view.methods = cls.methods # type: ignore
102 view.provide_automatic_options = cls.provide_automatic_options # type: ignore
103 return view
106class MethodViewType(type):
107 """Metaclass for :class:`MethodView` that determines what methods the view
108 defines.
109 """
111 def __init__(cls, name, bases, d):
112 super().__init__(name, bases, d)
114 if "methods" not in d:
115 methods = set()
117 for base in bases:
118 if getattr(base, "methods", None):
119 methods.update(base.methods)
121 for key in http_method_funcs:
122 if hasattr(cls, key):
123 methods.add(key.upper())
125 # If we have no method at all in there we don't want to add a
126 # method list. This is for instance the case for the base class
127 # or another subclass of a base method view that does not introduce
128 # new methods.
129 if methods:
130 cls.methods = methods
133class MethodView(View, metaclass=MethodViewType):
134 """A class-based view that dispatches request methods to the corresponding
135 class methods. For example, if you implement a ``get`` method, it will be
136 used to handle ``GET`` requests. ::
138 class CounterAPI(MethodView):
139 def get(self):
140 return session.get('counter', 0)
142 def post(self):
143 session['counter'] = session.get('counter', 0) + 1
144 return 'OK'
146 app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter'))
147 """
149 def dispatch_request(self, *args: t.Any, **kwargs: t.Any) -> ft.ResponseReturnValue:
150 meth = getattr(self, request.method.lower(), None)
152 # If the request method is HEAD and we don't have a handler for it
153 # retry with GET.
154 if meth is None and request.method == "HEAD":
155 meth = getattr(self, "get", None)
157 assert meth is not None, f"Unimplemented method {request.method!r}"
158 return current_app.ensure_sync(meth)(*args, **kwargs)