Blueprints and Views

    A is a way to organize a group of related views andother code. Rather than registering views and other code directly withan application, they are registered with a blueprint. Then the blueprintis registered with the application when it is available in the factoryfunction.

    Flaskr will have two blueprints, one for authentication functions andone for the blog posts functions. The code for each blueprint will goin a separate module. Since the blog needs to know about authentication,you’ll write the authentication one first.

    flaskr/auth.py

    This creates a Blueprint named 'auth'. Like the applicationobject, the blueprint needs to know where it’s defined, so nameis passed as the second argument. The url_prefix will be prependedto all the URLs associated with the blueprint.

    Import and register the blueprint from the factory using. Place thenew code at the end of the factory function before returning the app.

    flaskr/init.py

    1. def create_app():
    2. app = ...
    3. # existing code omitted
    4.  
    5. from . import auth
    6. app.register_blueprint(auth.bp)
    7.  
    8. return app

    The authentication blueprint will have views to register new users andto log in and log out.

    The First View: Register

    When the user visits the /auth/register URL, the register viewwill return HTML with a form for them to fill out. When they submitthe form, it will validate their input and either show the form againwith an error message or create the new user and go to the login page.

    For now you will just write the view code. On the next page, you’llwrite templates to generate the HTML form.

    flaskr/auth.py

    1. @bp.route('/register', methods=('GET', 'POST'))def register(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] db = get_db() error = None

    2.     if not username:
    3.         error = 'Username is required.'
    4.     elif not password:
    5.         error = 'Password is required.'
    6.     elif db.execute(
    7.         'SELECT id FROM user WHERE username = ?', (username,)
    8.     ).fetchone() is not None:
    9.         error = 'User {} is already registered.'.format(username)
    10.     if error is None:
    11.         db.execute(
    12.             'INSERT INTO user (username, password) VALUES (?, ?)',
    13.         )
    14.         db.commit()
    15.         return redirect(url_for('auth.login'))
    16.     flash(error)
    17. return render_template('auth/register.html')

    Here’s what the register view function is doing:

    • @bp.route associates the URL /registerwith the register view function. When Flask receives a requestto , it will call the register view and usethe return value as the response.

    • If the user submitted the form, will be 'POST'. In thiscase, start validating the input.

    • Validate that username and password are not empty.

    • Validate that username is not already registered by querying thedatabase and checking if a result is returned.db.execute takes a SQL querywith ? placeholders for any user input, and a tuple of valuesto replace the placeholders with. The database library will takecare of escaping the values so you are not vulnerable to aSQL injection attack.

    returns one row from the query.If the query returned no results, it returns None. Later,fetchall() is used, which returns a list ofall results.

    • If validation succeeds, insert the new user data into the database.For security, passwords should never be stored in the databasedirectly. Instead, is used tosecurely hash the password, and that hash is stored. Since thisquery modifies data, db.commit()needs to be called afterwards to save the changes.

    • After storing the user, they are redirected to the login page. generates the URL for the login view based on itsname. This is preferable to writing the URL directly as it allowsyou to change the URL later without changing all code that links toit. redirect() generates a redirect response to the generatedURL.

    • If validation fails, the error is shown to the user. stores messages that can be retrieved when rendering the template.

    • When the user initially navigates to auth/register, orthere was a validation error, an HTML page with the registrationform should be shown. render_template() will render a templatecontaining the HTML, which you’ll write in the next step of thetutorial.

    This view follows the same pattern as the register view above.

    flaskr/auth.py

    1. @bp.route('/login', methods=('GET', 'POST'))def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] db = get_db() error = None user = db.execute( 'SELECT * FROM user WHERE username = ?', (username,) ).fetchone()

    2.     if user is None:
    3.     elif not check_password_hash(user['password'], password):
    4.         error = 'Incorrect password.'
    5.     if error is None:
    6.         session['user_id'] = user['id']
    7.         return redirect(url_for('index'))
    8. return render_template('auth/login.html')

    There are a few differences from the register view:

    • The user is queried first and stored in a variable for later use.

    • check_password_hash() hashes the submittedpassword in the same way as the stored hash and securely comparesthem. If they match, the password is valid.

    • is a dict that stores data across requests.When validation succeeds, the user’s id is stored in a newsession. The data is stored in a cookie that is sent to thebrowser, and the browser then sends it back with subsequent requests.Flask securely signs the data so that it can’t be tampered with.

    flaskr/auth.py

    1. @bp.before_app_requestdef load_logged_in_user(): user_id = session.get('user_id')

    2. if user_id is None:
    3.     g.user = None
    4. else:
    5.     g.user = get_db().execute(
    6.         'SELECT * FROM user WHERE id = ?', (user_id,)
    7.     ).fetchone()

    bp.before_app_request() registersa function that runs before the view function, no matter what URL isrequested. load_logged_in_user checks if a user id is stored in the and gets that user’s data from the database, storing iton g.user, which lasts for the length of the request. Ifthere is no user id, or if the id doesn’t exist, g.user will beNone.

    Logout

    To log out, you need to remove the user id from the session.Then load_logged_in_user won’t load a user on subsequent requests.

    flaskr/auth.py

      Creating, editing, and deleting blog posts will require a user to belogged in. A decorator can be used to check this for each view it’sapplied to.

      flaskr/auth.py

      This decorator returns a new view function that wraps the original viewit’s applied to. The new function checks if a user is loaded andredirects to the login page otherwise. If a user is loaded the originalview is called and continues normally. You’ll use this decorator whenwriting the blog views.

      Endpoints and URLs

      The url_for() function generates the URL to a view based on a nameand arguments. The name associated with a view is also called theendpoint, and by default it’s the same as the name of the viewfunction.

      For example, the hello() view that was added to the appfactory earlier in the tutorial has the name 'hello' and can belinked to with url_for('hello'). If it took an argument, whichyou’ll see later, it would be linked to usingurl_for('hello', who='World').

      When using a blueprint, the name of the blueprint is prepended to thename of the function, so the endpoint for the login function youwrote above is 'auth.login' because you added it to the blueprint.

      Continue to .