Run `example4.py` and work with it in a browser. Here's the code: ``` #!python {! flask2/example4.py !} ``` ## Constructing URLs Constructing a URL doesn't seem hard, but nevertheless, Flask provides a function to do so, called `url_for`, which you should use instead of doing it yourself. This is important to make your Flask code *portable*_ and more easily *deployed*. The argument is *not*_ the URL but rather the *name* of the handler function as a *string*. (In practice, these are often the same, but not always.) In the example, look for `/urls/`: ```python @app.route('/urls/') def urls(): hrefs = [ url_for('print_stmt'), url_for('console_error'), url_for('no_view'), url_for('country', city='London'), url_for('sqrt', n=18), url_for('sturdy'), url_for('fragile'), # with Jinja2 templating url_for('hello'), url_for('tim'), url_for('things'), url_for('reading'), url_for('render_styled') ] links = [ '

{h}'.format(h=h) for h in hrefs ] return ''.join(links) ``` It's cool that with `n=18`, the number is coerced to a float. Note that the following does *not* work, because the city is not optional in the `country` function: ```python url_for('country') ``` ## Errors When you're coding, it's possible that you will make a mistake. I always do. So it's good to know what happens and how to fix it. ## Print Statements We often debug by putting print statements in our code. Print statements in your code go to the shell (log): ```python @app.route('/print_stmt/') def print_stmt(): n = random.randint(1,10) print('n is ',n) return '

Can you guess your lucky number?' ``` ## Console Errors Sometimes, your code may throw an exception. The backtrace will go to the browser. Even better, you can invoke a debugger and actually poke around in stack frames, printing variables, though you will need a *PIN*, which is printed to the log. Try the following url. Look for `/console_error/` in the code. ```python @app.route('/console_error/') def console_error(): # this doesn't work roman = ['x','v','i','c','m','d','l'] s = random.choice(roman) n = int(s) return ('{roman} converts to {arabic}' .format(roman=s,arabic=n)) ``` To use it: * Find the stack frame you're interested in. * Click on the little black rectangle to the right that looks like a terminal/shell window * Enter your pin * Find the console * Enter commands like `` print(s) `` and even `` random.choice(roman) ``. Even just `` s `` will show you the value. * Try `` dump() `` and `` dump(roman) `` * Go to the very top stack frame, then * Try `` request `` and `` dump(request) `` ## No View One of the more common errors (at least for me) that can be hard to debug is when the handler doesn't return some HTML code. Try the following. Do you see what the error is? ```python @app.route('/no_view/') def no_view(): n = random.randint(1,10) if n < 9: print('

Keep trying, you only scored a {n}'.format(n=n)) else: return '

Wow, you scored a 10!!' ``` ## Canonical URLs A canonical URL is just one that is preferred for indexing, etc. It helps search engines know that two URLs have the same content and which URL is preferred. In Flask, there's another consideration: * If the `route()` is given a URL that ends with a slash, the routing code will respond to URLs both with and without a slash. * If the `route()` is given a URL that ends without a slash, the routing code will only respond to the URLs without a slash. **trailing slash is preferred** See these examples: ```python @app.route('/sturdy/') def sturdy(): return '

Well done!' @app.route('/fragile') def fragile(): return '

Maybe?' ``` To understand what really happens, let's use the *network* tab in the *Chrome Developer* tools. Try each URL both with and without the trailing slash. Notice that: * If you type '/sturdy' into the browser URL, flask *redirects* to the *canonical* one ('/sturdy/'. So both work. * If you type '/fragile/' into the browser URL, you get an error. In short: use trailing slashes in your URLs, particularly if there is sub-structure (so that it acts like a directory rather than a file). ## Jinja2 With Flask and Jinja2, all the templates in the `templates` folder are read into memory and parsed when the flask application loads. This is great for efficiency. It does, however, mean that an error in any template file, even one you're not using, can cause errors when your app loads. Fair warning. ## The Template Language Now let's turn to the template language. These examples are in your downloaded copy of `flask2`: ## Variables Double braces surround python variables, and the variable's value is inserted: Look at the [templates/hello.html](flask2/templates/hello.html) file. ```html {! flask2/templates/hello.html !} ``` Here's the route that uses that template: ```python @app.route('/hello/') def hello(): nom = random.randomElt(['Inigo Montoya', 'Harry','Ron','Hermione']) return render_template('hello.html', name = nom) ``` ## Dictionaries You can also access elements of dictionaries: ```python @app.route('/tim/') def tim(): art = {'name': 'Arthur, King of the Britons', 'quest': 'To seek the grail', 'color': 'blue'} return render_template('tim.html', dic = art) ``` Look at the [templates/tim.html](flask2/templates/tim.html) file: ```html {! flask2/templates/tim.html !} ``` Notice the shortcut syntax (like JavaScript's): you can say `dic.key` instead of `dic['key']`. ## Arrays If you have an list of data, you can loop over it. Here's a template that has a loop that uses Python syntax: ```python @app.route('/things/') def things(): favs = ['Raindrops on roses', 'whiskers on kittens', 'bright copper kettles', 'warm woolen mittens', 'brown paper packages tied up with strings'] return render_template('things.html', things = favs) ``` Look at the [templates/things.html](flask2/templates/things.html) file. ```html {! flask2/templates/things.html !} ``` ## Static URLs Supporting files, like CSS and JS are served out of a subdirectory named `static`. Here is our [example static directory](flask2/static/) Here's an example, styled for Halloween: ```HTML {! flask2/templates/trio-styled.html !} ``` Here's the very short route that renders that template. ```python @app.route('/render_styled/') def render_styled(): return render_template('trio-styled.html', a='Harry',b='Ron',c='Hermione') ```