Combine Flask Blueprint Pages, Jinja Templates, and Databases

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

Building pages using our models

Now that we have the ability to retrieve stored products from our database within the console, we can use SQLAlchemy to render pages that display the data for products we have.

The two routes that we will need to implement to build out our (read only) product catalog is one to list all of the products and one to display the product details.

Setting up a blueprint

As we saw earlier, blueprints are a useful organizational tool in Flask applications to split up your routes. A common pattern is to organize blueprints by the models they work with.

How large should blueprints be? As always, it varies. As a rule of thumb, if your blueprint only contains a single route, you may have gone too granular. If your blueprint is defining over ten routes, it may be too large and could benefit from splitting it up. A common way to organize blueprints is by the URL path and/or model they work with. If you blueprint is pushing 250+ lines of code, you may benefit from some additional modularization.

Here we can create a blueprint for our product catalog called products.py. To do that, we'll need to create a blueprints folder in the yumroad folder. Then inside of the blueprints folder, create a file called products.py where our blueprint and routes will be defined.

At this point, your folder structure should like this:

yumroad/
    blueprints/
        products.py
    __init__.py
    config.py
    models.py
requirements.txt
conftest.py

Now within products.py, we'll set up the basics of a blueprint called products just like we did in Chapter 3.

from flask import Blueprint, render_template

products = Blueprint('products', __name__)

@products.route('/')
def index():
    return "All Products: Coming soon"

Recall: The first argument of the Blueprint initialization is the name that will used when trying to get the route path when using url_for. In this case it will be products

Now we need to import and register the blueprint within our create_app function (in yumroad/__init__.py). While we are at it, we can also prefix the URL of all the routes in this blueprint with /products to start building up the (conceptual) routing layout for our application.

yumroad-app/yumroad/\_\_init\_\_.py
from flask import Flask, render_template

from yumroad.blueprints.products import products
from yumroad.config import configurations
from yumroad.extensions import (db)


def create_app(environment_name='dev'):
    app = Flask(__name__)
    app.config.from_object(configurations[environment_name])
    db.init_app(app)
    app.register_blueprint(products, url_prefix="/product")
    return app

# FLASK_DEBUG=true FLASK_APP="yumroad:create_app" flask run

At this point, we can now run the application using the flask run command. In our terminal, within the top level folder (that contains yumroad and requirements.txt), run the following command to set up our server FLASK_ENV=development FLASK_APP="yumroad:create_app" flask run.

Reminder: You can export the environment variables into your terminal session to only have to type flask run or you can create a script to launch your development environment.

export FLASK_ENV=development
export FLASK_APP=yumroad:create_app
# Now you can just run the following command in your terminal
flask run

Setting up Jinja templates

In chapter 3, we talked about how to organize templates with blueprints. Since this is a new application, we'll have to set up that structure again, starting with a base_layout.html file.

Within our yumroad folder, create a folder called templates and create a base_layout.html file within it.

This file will define the outlines of a basic HTML page (using Bootstrap for basic styling) with two blocks for templates that use this base to customize the content and page title.

yumroad-app/yumroad/templates/base_layout.html
<!DOCTYPE html>
<html>
    <head>
        <title>
            {% block title %}{% endblock %} Yumroad
        </title>

        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous">
    </head>
    <body>
        <nav class="navbar navbar-light bg-light justify-content-between">
            <a class="navbar-brand">Yumroad</a>
        </nav>

This lesson preview is part of the Fullstack Flask: Build a Complete SaaS App with Flask course and can be unlocked immediately with a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to Fullstack Flask: Build a Complete SaaS App with Flask with a single-time purchase.

Thumbnail for the \newline course Fullstack Flask: Build a Complete SaaS App with Flask
  • [00:00 - 00:24] Now that we have the ability to retrieve stored products from our database, we can use SQL/QM to render pages in our web application that display the data in our database. The two routes we'll need to implement for this in our catalog is going to be a listing of all of the products that we have in our database, as well as one that displays the details of an individual product.

    [00:25 - 00:38] Now we can go inside of here and define routes in our code editor, but as we learned earlier, blueprints are just a better organizational structure. And so what we're going to do is we're going to create a blueprint.

    [00:39 - 00:51] And in our blueprints folder, we're going to call one products.py. Within products.py, we're going to go ahead and import a blueprint from Flask.

    [00:52 - 01:00] So from Flask, import blueprint, and then we're going to create our own group. And so we're going to say products is equal to blueprint.

    [01:01 - 01:14] And we're going to give a name to our blueprint, which can just be products, and give it the name of the module we're in. Now that we're here, we're going to have to create a route for the index where we'd list all of the products.

    [01:15 - 01:28] And we're going to create a route for the details of an individual product, which can accept a product ID. Then let's return the details for that product ID.

    [01:29 - 01:39] Okay, now that we have these, we can tie routes to them. So for this one, we can say products dot route, and we can just do slash product like that.

    [01:40 - 01:52] Or we can just say slash and give it a prefix when we register the blueprint. Then for this one, we're going to say, this is going to be an integer because that's the type in our database.

    [01:53 - 02:01] And then we're going to pass in that parameter. Now that we have this blueprint, we can go in init and import it.

    [02:02 - 02:12] So we can say from yumroad dot blueprint dot products, which is this module right here. We're going to go ahead and import products.

    [02:13 - 02:16] Great. Our next step is we're going to go and register that blueprint.

    [02:17 - 02:21] So we're going to say app dot register blueprint. And then we're going to pass in a URL prefix here.

    [02:22 - 02:31] The URL prefix will be slash product. This means that if we go to our application slash product, we'll be sent to this page by default.

    [02:32 - 02:38] And if we go to slash product slash one, we'll be requesting the details of the product with ID. What?

    [02:39 - 02:46] Let's go and check if this application runs and see what it returns. So in our terminal, we're going to run class run.

    [02:47 - 02:53] That runs our application. Then in Firefox, our browser, we can load the application.

    [02:54 - 03:02] Now again, the root of the application doesn't have any routes registered to it . So we're going to go into product slash one to view a particular product.

    [03:03 - 03:13] And then if we just go back to just slash product, you'll see it renders all of the products. One convenient trick is to run the command flask routes.

    [03:14 - 03:27] And what that does is it shows us all of the routes that we've set up and registered. Now going back in VS code, we probably want to fetch that individual product ID .

    [03:28 - 03:39] So we're going to go ahead and import that model from our models file. Now that we have a product, we can go ahead and issue a query.

    [03:40 - 03:48] So we can say product query dot get of product ID. So we can call this a product.

    [03:49 - 03:55] And then what we can do is maybe just return the product name. All right, let's see how that looks.

    [03:56 - 04:03] Okay, so I pulled up product ID one, and that seems like to works. What happens if I pull up product ID two?

    [04:04 - 04:12] It's going to go ahead and give me an error. So what we want to do in that case is we actually want to render a 404 if the product does in fact.

    [04:13 - 04:23] So we can do abort. And we can say if not product abort 404.

    [04:24 - 04:32] Let's go and see what happens now. So we see product dash one, which means is a product that exists right now.

    [04:33 - 04:40] And product as to return a 404, which is exactly what we want. Now it turns out there's a convenient shortcut for us to do this.

    [04:41 - 04:58] If we go to VS code, we can actually just say get or 404, which is something that flash SQL Academy provides for us and allows us to not have to write that logic . So this is almost a cool one.

    [04:59 - 05:08] Now up here, we could do something like product query.count. This would give us the number of products.

    [05:09 - 05:21] Or we can do all and get all of the products. But now we're faced with a decision of how to actually render this stuff to our browser.

    [05:22 - 05:25] It's probably not enough to just render the name. We're going to have to create some templates.

    [05:26 - 05:32] So that's what we're going to do next. In the YumRoad folder, go ahead and create a templates file.

    [05:33 - 05:46] And in this, we're going to create a template file for the details page, as well as for the index page. Now, because we're going to have multiple pages here, I think it makes sense to have a base layout file, just like in our previous projects.

    [05:47 - 05:57] Here, we're going to define some base content that will help us style our application. So for example, one thing we'll need is a title for all of our applications.

    [05:58 - 06:06] For now, I'm going to call that YumRoad. Then we're going to have to define some styles and also some page layout.

    [06:07 - 06:12] So I'm going to start with our page layout here. We're going to want a nav, something like that.

    [06:13 - 06:22] And then we're going to want a div, which will be our content. So we'll say this will be a container and we'll give it some bootstrap padding.

    [06:23 - 06:30] And then we're going to define our block here. So we're going to say a block for content and the block.

    [06:31 - 06:39] This is what our templates will use. Similarly, we're going to create a block up here for some titles that the application can customize.

    [06:40 - 06:55] Okay, and I'm going to leave that YumRoad part there so that that's always there on our application. Next is I'm going to paste in some styles here.

    [06:56 - 07:00] Exactly like our previous template. All right, I've pasted in some styles there.

    [07:01 - 07:07] Next up is a nav bar. All right, I have a nav bar here similar to the one we created before.

    [07:08 - 07:12] And it just gives us some base styling. This one, I've just imported a version of bootstrap.

    [07:13 - 07:19] You can use whatever CSS framework you think is best. But for this example, and going forward in this course, we're going to use boot strap.

    [07:20 - 07:31] Okay, now that we have a base layout, let's go and create a folder within our templates project called products. The idea here is this is named similarly to our blueprint.

    [07:32 - 07:39] So it's easy for us to figure out which template goes where. Then we're going to create one called details dot html.

    [07:40 - 07:50] And we're going to have to say that it extends from base layout dot html. And then we can define our blocks.

    [07:51 - 08:02] So we can say our block is content and we're going to end the block. Now we're going to have to be passed in the product here if we want to say anything interesting.

    [08:03 - 08:14] So what we can do is we can also get the title in here as well. So for our title block, what we can say is it's going to be product dot name.

    [08:15 - 08:32] And within the content of the page, we can do some basic stuff that helps us show some details. So something that we could do here is we can say here's an h1 tag where we put the product name.

    [08:33 - 08:49] And maybe we'll separate that with the line and then we'll say here's the product description. And we'll again separate that a little bit.

    [08:50 - 08:57] And then we'll say here's a button. And all our button will do is it'll say purchase coming soon.

    [08:58 - 09:13] And we can add some styling here to say, All right, let's check out how that looks. To do that, we're going to have to actually render the template here.

    [09:14 - 09:20] So we're going to say render template. And then we're going to pass in the name of the template.

    [09:21 - 09:32] And the name of our template is going to be product slash details dot html. And we're going to have to pass in the product here.

    [09:33 - 09:45] All right, so we're ready to check this out. All right, I just loaded the page and now we see our details.

    [09:46 - 09:54] And if I go to product slash two, it doesn't show up. Now this 404 page just doesn't look very good.

    [09:55 - 10:03] So let's go ahead and spruce that up a little bit by creating our own version. What we're going to do is we're going to create one specifically if our product is not found.

    [10:04 - 10:09] So that means we're going to create a template here. And the template we're going to use will be similar.

    [10:10 - 10:25] We're going to maybe change the name and we're going to say product not found. And maybe we'll say something like whoops, we couldn't find that product.

    [10:26 - 10:43] And what we'll want to do is we'll probably want to link back to a specific page, maybe the index page so we can take users somewhere. So what we can do here is we can set a link and make that link equal to the URL for the index page.

    [10:44 - 10:53] Okay, now we have a link that takes us back to our page. We can say view all products.

    [10:54 - 11:12] And now our job is to register that flash should use this file when rendering a 404 page. So going back here, the way we can do that is by telling the blueprint that we 're going to specify an error handler here and specifically we wanted to capture 404 hours.

    [11:13 - 11:26] So we're going to say something like not found. Flass is going to pass in an exception by default, which we're not really going to use because all we want to do is render the template that's we've just created here .

    [11:27 - 11:32] And we want to render it with a 404 code as well. All right, let's check it out in our browser now.

    [11:33 - 11:40] Okay, that looks a lot better. And then if we click on view price, we're taking back that page.

    [11:41 - 11:50] So now let's produce that page up. Going back in our templates, let's go ahead and create a new template called index.html.

    [11:51 - 11:59] And here we're going to suit details. And instead of listing one particular product, what we want to do is pass in an array of products.

    [12:00 - 12:06] So let's call this all products instead. And we can render a table here.

    [12:07 - 12:27] So a table looks something like this where we have a table, a T head, and a table body. And then what we're going to do within the table body is we're going to say for every product in products, which will pass down from our template, we're going to render a row.

    [12:28 - 12:46] So our table row could look something like this where we're passing in details. So we could say this is going to be the name of the product.

    [12:47 - 13:00] And then maybe we can do one other thing that's product.description. Okay. And then here we can give it some titles.

    [13:01 - 13:14] So we can say this is with name and description. All right, let's see if that worked.

    [13:15 - 13:23] We'll add some basic styling here by giving this a table class. All right.

    [13:24 - 13:33] Going back in our products, we're going to have to pass this information into our template. So we've already fetched all of our code and all of our models.

    [13:34 - 13:44] So let's go and say products/index.html. And then we can say products is equal to all products there.

    [13:45 - 13:52] Going back to Firefox, let's see what happens. Okay. So we're able to load that particular page, which is great.

    [13:53 - 14:00] One thing we should probably do is add a link here so that we can view and click in that product details. So let's do that in our template.

    [14:01 - 14:17] Here we can add an a tag with a link to the product. So we can say something like urlforproducts. details.

    [14:18 - 14:29] And then we can pass in the product id as product.id. And then within here we can call it product.me.

    [14:30 - 14:37] All right. So here I've added a urlfor and that allows us to get a link.

    [14:38 - 14:51] So switching back into our browser and refreshing, we now have a link that allows us to go to the product page. Now the last part of this is building out our test suite.

    [14:52 - 15:13] We're going to finish our test suite in the next part of this video. [ Inaudible ]