From 48362903fed0b9010298ed7e194fddd09ea1d9bc Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Fri, 2 May 2014 12:55:41 +0530 Subject: new post on using flask's url_for function --- content/development/note_to_self_url_for_flask.rst | 134 +++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 content/development/note_to_self_url_for_flask.rst (limited to 'content') diff --git a/content/development/note_to_self_url_for_flask.rst b/content/development/note_to_self_url_for_flask.rst new file mode 100644 index 0000000..7b921a4 --- /dev/null +++ b/content/development/note_to_self_url_for_flask.rst @@ -0,0 +1,134 @@ +Note to Self: How to use url_for in Flask application +##################################################### + +:date: 2014-05-02 11:50 +:slug: using-url-for-in-flask +:tags: python, programming, flask, webapp, notetoself +:author: copyninja +:summary: Just a note to self on how to use url_for in Flask + application and jinja2 template system. + + +`url_for` is normally used to avoid hard coding of URL in Flask based +webapps. I've been using it in SILPA `port +`_ written using Flask +by. But this was written long back and written as POC to show for +Santhosh author of SILPA app which even managed to go into production +:-). Recently I started organizing the code and started using +`flask.Blueprint` which was previously written using +`flask.views.MethodView` class and here I started facing problem with +old templates. I'm just gonna explain what is the difference when +using url_for with `MethodView` and `Blueprint`. + +Lets have some sample code for `MethodView` first, this code is from +`Flask` documentation. + +.. code-block:: python + + 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')) + + +Here we have a view which is derived from `MethodView` which has logic +on how to handle the requests. We use `add_url_rule` to register rule +to handle `/users/` url end point and we pass the class as view +function which is done by `as_view` class method and we can refer this +method in our Jinja2 templates using name `users`. So a statement like + +.. code-block:: python + + url_for('users') + + +in our templates will be converted to `/users/` URL when page is +rendered to client by Flask. + +Now when I replaced the `MethodView` in favor of `Blueprint` I started +getting up `werkzeug.routing.BuildError` thrown on my face and I had +no clue why!. Yeah I know I'm bad at reading documentation but even +after reading documentation I was still thinking that + +.. code-block:: python + + url_for('/') + +should return me a proper URL and I was wondering why its +failing. Finally after re-reading documentation for `url_for` it was +becoming clear to me, `url_for` definition looks like below + +.. code-block:: python + + flask.url_for(endpoint, **values) + +Here `endpoint` is actually a function which is supposed to be serving +the URL and `**values` is the arguments for this function. The URL in +question should be defined in python code using decorator. In my case +following is the new function serving the web pages for SILPA. + +.. code-block:: python + + bp = Blueprint('frontend', __name__) + + + @bp.route(_BASE_URL, defaults={'page': 'index.html'}) + @bp.route(_BASE_URL + '') + def serve_pages(page): + if page == "index.html": + return render_template('index.html', title='SILPA', + main_page=_BASE_URL, + modules=_modulename_to_display) + elif page == "License": + return render_template('license.html', title='SILPA License', + main_page=_BASE_URL, + modules=_modulename_to_display) + elif page == "Credits": + return render_template('credits.html', title='Credits', + main_page=_BASE_URL, + modules=_modulename_to_display) + elif page == "Contact": + return render_template('contact.html', title='Contact SILPA Team', + main_page=_BASE_URL, + modules=_modulename_to_display) + else: + # modules requested!. + if page in _display_module_map: + return render_template(_display_module_map[page] + '.html', + title=page, main_page=_BASE_URL, + modules=_modulename_to_display) + else: + # Did we encounter something which is not registered by us? + return abort(404) + + +You can ignore function code but just note the decorators, here I'm +registering the function `serve_pages` with `page` as argument for URL +patterns `/` and `/`, `_BASE_URL` here is mount point of +application it can be just `/` or `/mountpoint` depending on that URL +registered changes. Now I need to modify code for all `url_for` in my +template to look like below + +.. code-block:: python + + url_for('.serve_pages', page='/License') # for /License + url_for('.serve_pages') # which will turn in to /index.html + +The `.` in front of function is for referring current Blueprint, in my +case the Flask will consider it as `frontend.serve_pages` as function +name and generates appropriate URL at run time. + +So my fault was misunderstanding `endpoint` argument as URL endpoint +but where as its actually function name supposed to serve the +page. But when using MethodViews I can simply convert class to a +function with my preferred name just like `UserAPI.as_view('/')` so +`url_for('/')` just works. -- cgit v1.2.3