Skip to content
This repository
Browse code

Added class based view documentation

  • Loading branch information...
commit b36d7b3288442173bcc999ebb4310674869a526d 1 parent 775caf7
Armin Ronacher authored June 28, 2011
13  docs/api.rst
Source Rendered
@@ -413,3 +413,16 @@ Signals
413 413
       operations, including connecting.
414 414
 
415 415
 .. _blinker: http://pypi.python.org/pypi/blinker
  416
+
  417
+Class Based Views
  418
+-----------------
  419
+
  420
+.. versionadded:: 0.7
  421
+
  422
+.. currentmodule:: None
  423
+
  424
+.. autoclass:: flask.views.View
  425
+   :members:
  426
+
  427
+.. autoclass:: flask.views.MethodView
  428
+   :members:
1  docs/contents.rst.inc
@@ -17,6 +17,7 @@ instructions for web development with Flask.
17 17
    errorhandling
18 18
    config
19 19
    signals
  20
+   views
20 21
    reqcontext
21 22
    blueprints
22 23
    shell
137  docs/views.rst
Source Rendered
... ...
@@ -0,0 +1,137 @@
  1
+.. _views:
  2
+
  3
+Pluggable Views
  4
+===============
  5
+
  6
+.. versionadded:: 0.7
  7
+
  8
+Flask 0.7 introduces pluggable views inspired by the generic views from
  9
+Django which are based on classes instead of functions.  The main
  10
+intention is that you can replace parts of the implementations and this
  11
+way have customizable pluggable views.
  12
+
  13
+Basic Principle
  14
+---------------
  15
+
  16
+Consider you have a function that loads a list of objects from the
  17
+database and renders into a template::
  18
+
  19
+    @app.route('/users/')
  20
+    def show_users(page):
  21
+        users = User.query.all()
  22
+        return render_template('users.html', users=users)
  23
+
  24
+This is simple and flexible, but if you want to provide this view in a
  25
+generic fashion that can be adapted to other models and templates as well
  26
+you might want more flexibility.  This is where pluggable class based
  27
+views come into place.  As the first step to convert this into a class
  28
+based view you would do this::
  29
+
  30
+
  31
+    from flask.views import View
  32
+
  33
+    class ShowUsers(View):
  34
+
  35
+        def dispatch_request(self):
  36
+            users = User.query.all()
  37
+            return render_template('users.html', objects=users)
  38
+
  39
+    app.add_url_rule('/users/', ShowUsers.as_view('show_users'))
  40
+
  41
+As you can see what you have to do is to create a subclass of
  42
+:class:`flask.views.View` and implement
  43
+:meth:`~flask.views.View.dispatch_request`.  Then we have to convert that
  44
+class into an actual view function by using the
  45
+:meth:`~flask.views.View.as_view` class method.  The string you pass to
  46
+that function is the name of the endpoint that view will then have.  But
  47
+this by itself is not helpful, so let's refactor the code a bit::
  48
+
  49
+    
  50
+    from flask.views import View
  51
+
  52
+    class ListView(View):
  53
+
  54
+        def get_template_name(self):
  55
+            raise NotImplementedError()
  56
+
  57
+        def render_template(self, context):
  58
+            return render_template(self.get_template_name(), **context)
  59
+
  60
+        def dispatch_request(self):
  61
+            context = {'objects': self.get_objects()}
  62
+            return self.render_template(context)
  63
+
  64
+    class UserView(ListView):
  65
+
  66
+        def get_template_name(self):
  67
+            return 'users.html'
  68
+
  69
+        def get_objects(self):
  70
+            return User.query.all()
  71
+
  72
+This of course is not that helpful for such a small example, but it's good
  73
+enough to explain the basic principle.  When you have a class based view
  74
+the question comes up what `self` points to.  The way this works is that
  75
+whenever the request is dispatched a new instance of the class is created
  76
+and the :meth:`~flask.views.View.dispatch_request` method is called with
  77
+the parameters from the URL rule.  The class itself is instanciated with
  78
+the parameters passed to the :meth:`~flask.views.View.as_view` function.
  79
+For instance you can write a class like this::
  80
+
  81
+    class RenderTemplateView(View):
  82
+        def __init__(self, template_name):
  83
+            self.template_name = template_name
  84
+        def dispatch_request(self):
  85
+            return render_template(self.template_name)
  86
+
  87
+And then you can register it like this::
  88
+
  89
+    app.add_url_view('/about', RenderTemplateView.as_view(
  90
+        'about_page', template_name='about.html'))
  91
+
  92
+Method Hints
  93
+------------
  94
+
  95
+Pluggable views are attached to the application like a regular function by
  96
+either using :func:`~flask.Flask.route` or better
  97
+:meth:`~flask.Flask.add_url_rule`.  That however also means that you would
  98
+have to provide the names of the HTTP methods the view supports when you
  99
+attach this.  In order to move that information to the class you can
  100
+provide a :attr:`~flask.views.View.methods` attribute that has this
  101
+information::
  102
+
  103
+    class MyView(View):
  104
+        methods = ['GET', 'POST']
  105
+
  106
+        def dispatch_request(self):
  107
+            if request.method == 'POST':
  108
+                ...
  109
+            ...
  110
+
  111
+    app.add_url_view('/myview', MyView.as_view('myview'))
  112
+
  113
+Method Based Dispatching
  114
+------------------------
  115
+
  116
+For RESTful APIs it's especially helpful to execute a different function
  117
+for each HTTP method.  With the :class:`flask.views.MethodView` you can
  118
+easily do that.  Each HTTP method maps to a function with the same name
  119
+(just in lowercase)::
  120
+
  121
+    from flask.views import MethodView
  122
+
  123
+    class UserAPI(MethodView):
  124
+
  125
+        def get(self):
  126
+            users = User.query.all()
  127
+            ...
  128
+
  129
+        def post(self):
  130
+            user = User.from_form_data(request.form)
  131
+            ...
  132
+
  133
+    app.add_url_view('/users/', UserAPI.as_view('users'))
  134
+
  135
+That way you also don't have to provide the
  136
+:attr:`~flask.views.View.methods` attribute.  It's automatically set based
  137
+on the methods defined in the class.
41  tests/flask_tests.py
@@ -1787,8 +1787,49 @@ def post(self):
1787 1787
                 return 'POST'
1788 1788
 
1789 1789
         app.add_url_rule('/', view_func=Index.as_view('index'))
  1790
+
  1791
+        self.common_test(app)
  1792
+
  1793
+    def test_view_patching(self):
  1794
+        app = flask.Flask(__name__)
  1795
+
  1796
+        class Index(flask.views.MethodView):
  1797
+            def get(self):
  1798
+                1/0
  1799
+            def post(self):
  1800
+                1/0
  1801
+
  1802
+        class Other(Index):
  1803
+            def get(self):
  1804
+                return 'GET'
  1805
+            def post(self):
  1806
+                return 'POST'
  1807
+
  1808
+        view = Index.as_view('index')
  1809
+        view.view_class = Other
  1810
+        app.add_url_rule('/', view_func=view)
1790 1811
         self.common_test(app)
1791 1812
 
  1813
+    def test_view_inheritance(self):
  1814
+        app = flask.Flask(__name__)
  1815
+
  1816
+        class Index(flask.views.MethodView):
  1817
+            def get(self):
  1818
+                return 'GET'
  1819
+            def post(self):
  1820
+                return 'POST'
  1821
+
  1822
+        class BetterIndex(Index):
  1823
+            def delete(self):
  1824
+                return 'DELETE'
  1825
+
  1826
+        app.add_url_rule('/', view_func=BetterIndex.as_view('index'))
  1827
+        c = app.test_client()
  1828
+
  1829
+        meths = parse_set_header(c.open('/', method='OPTIONS').headers['Allow'])
  1830
+        self.assertEqual(sorted(meths), ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST'])
  1831
+
  1832
+
1792 1833
 class DeprecationsTestCase(unittest.TestCase):
1793 1834
 
1794 1835
     def test_init_jinja_globals(self):

0 notes on commit b36d7b3

Please sign in to comment.
Something went wrong with that request. Please try again.