From 82511a6fe2d29d50c1cdca4b2abb23ff681a1943 Mon Sep 17 00:00:00 2001 From: Sebastian Silva Date: Wed, 16 Nov 2011 07:56:19 +0000 Subject: Major improvements in IDE usability. --- (limited to 'app/static/doc/flask-docs/_sources/views.txt') diff --git a/app/static/doc/flask-docs/_sources/views.txt b/app/static/doc/flask-docs/_sources/views.txt new file mode 100644 index 0000000..441620a --- /dev/null +++ b/app/static/doc/flask-docs/_sources/views.txt @@ -0,0 +1,227 @@ +.. _views: + +Pluggable Views +=============== + +.. versionadded:: 0.7 + +Flask 0.7 introduces pluggable views inspired by the generic views from +Django which are based on classes instead of functions. The main +intention is that you can replace parts of the implementations and this +way have customizable pluggable views. + +Basic Principle +--------------- + +Consider you have a function that loads a list of objects from the +database and renders into a template:: + + @app.route('/users/') + def show_users(page): + users = User.query.all() + return render_template('users.html', users=users) + +This is simple and flexible, but if you want to provide this view in a +generic fashion that can be adapted to other models and templates as well +you might want more flexibility. This is where pluggable class based +views come into place. As the first step to convert this into a class +based view you would do this:: + + + from flask.views import View + + class ShowUsers(View): + + def dispatch_request(self): + users = User.query.all() + return render_template('users.html', objects=users) + + app.add_url_rule('/users/', ShowUsers.as_view('show_users')) + +As you can see what you have to do is to create a subclass of +:class:`flask.views.View` and implement +:meth:`~flask.views.View.dispatch_request`. Then we have to convert that +class into an actual view function by using the +:meth:`~flask.views.View.as_view` class method. The string you pass to +that function is the name of the endpoint that view will then have. But +this by itself is not helpful, so let's refactor the code a bit:: + + + from flask.views import View + + class ListView(View): + + def get_template_name(self): + raise NotImplementedError() + + def render_template(self, context): + return render_template(self.get_template_name(), **context) + + def dispatch_request(self): + context = {'objects': self.get_objects()} + return self.render_template(context) + + class UserView(ListView): + + def get_template_name(self): + return 'users.html' + + def get_objects(self): + return User.query.all() + +This of course is not that helpful for such a small example, but it's good +enough to explain the basic principle. When you have a class based view +the question comes up what `self` points to. The way this works is that +whenever the request is dispatched a new instance of the class is created +and the :meth:`~flask.views.View.dispatch_request` method is called with +the parameters from the URL rule. The class itself is instanciated with +the parameters passed to the :meth:`~flask.views.View.as_view` function. +For instance you can write a class like this:: + + class RenderTemplateView(View): + def __init__(self, template_name): + self.template_name = template_name + def dispatch_request(self): + return render_template(self.template_name) + +And then you can register it like this:: + + app.add_url_rule('/about', view_func=RenderTemplateView.as_view( + 'about_page', template_name='about.html')) + +Method Hints +------------ + +Pluggable views are attached to the application like a regular function by +either using :func:`~flask.Flask.route` or better +:meth:`~flask.Flask.add_url_rule`. That however also means that you would +have to provide the names of the HTTP methods the view supports when you +attach this. In order to move that information to the class you can +provide a :attr:`~flask.views.View.methods` attribute that has this +information:: + + class MyView(View): + methods = ['GET', 'POST'] + + def dispatch_request(self): + if request.method == 'POST': + ... + ... + + app.add_url_rule('/myview', view_func=MyView.as_view('myview')) + +Method Based Dispatching +------------------------ + +For RESTful APIs it's especially helpful to execute a different function +for each HTTP method. With the :class:`flask.views.MethodView` you can +easily do that. Each HTTP method maps to a function with the same name +(just in lowercase):: + + from flask.views import MethodView + + class UserAPI(MethodView): + + def get(self): + users = User.query.all() + ... + + def post(self): + user = User.from_form_data(request.form) + ... + + app.add_url_rule('/users/', view_func=UserAPI.as_view('users')) + +That way you also don't have to provide the +:attr:`~flask.views.View.methods` attribute. It's automatically set based +on the methods defined in the class. + +Decorating Views +---------------- + +Since the view class itself is not the view function that is added to the +routing system it does not make much sense to decorate the class itself. +Instead you either have to decorate the return value of +:meth:`~flask.views.View.as_view` by hand:: + + view = rate_limited(UserAPI.as_view('users')) + app.add_url_rule('/users/', view_func=view) + +Starting with Flask 0.8 there is also an alternative way where you can +specify a list of decorators to apply in the class declaration:: + + class UserAPI(MethodView): + decorators = [rate_limited] + +Due to the implicit self from the caller's perspective you cannot use +regular view decorators on the individual methods of the view however, +keep this in mind. + +Method Views for APIs +--------------------- + +Web APIs are often working very closely with HTTP verbs so it makes a lot +of sense to implement such an API based on the +:class:`~flask.views.MethodView`. That said, you will notice that the API +will require different URL rules that go to the same method view most of +the time. For instance consider that you are exposing a user object on +the web: + +=============== =============== ====================================== +URL Method Description +--------------- --------------- -------------------------------------- +``/users/`` ``GET`` Gives a list of all users +``/users/`` ``POST`` Creates a new user +``/users/`` ``GET`` Shows a single user +``/users/`` ``PUT`` Updates a single user +``/users/`` ``DELETE`` Deletes a single user +=============== =============== ====================================== + +So how would you go about doing that with the +:class:`~flask.views.MethodView`? The trick is to take advantage of the +fact that you can provide multiple rules to the same view. + +Let's assume for the moment the view would look like this:: + + class UserAPI(MethodView): + + def get(self, user_id): + if user_id is None: + # return a list of users + pass + else: + # expose a single user + pass + + def post(self): + # create a new user + pass + + def delete(self, user_id): + # delete a single user + pass + + def put(self, user_id): + # update a single user + pass + +So how do we hook this up with the routing system? By adding two rules +and explicitly mentioning the methods for each:: + + user_view = UserAPI.as_view('user_api') + app.add_url_rule('/users/', defaults={'user_id': None}, + view_func=user_view, methods=['GET', 'POST']) + app.add_url_rule('/users/', view_func=user_view, + methods=['GET', 'PUT', 'DELETE']) + +If you have a lot of APIs that look similar you can refactor that +registration code:: + + def register_api(view, endpoint, url, pk='id', pk_type='int'): + view_func = view.as_view(endpoint) + app.add_url_rule(url, defaults={pk: None}, + view_func=view_func, methods=['GET', 'POST']) + app.add_url_rule('%s<%s:%s>' % (url, pk), view_func=view_func, + methods=['GET', 'PUT', 'DELETE']) + + register_api(UserAPI, 'user_api', '/users/', pk='user_id') -- cgit v0.9.1