4: Flaskinni

Expectations

Flaskinni is a simple, pre-configured Flask app we can start customizing right away. In this unit we'll get your copy setup and study its structure.

Learning Targets

  • I can clone, configure and preview my copy of Flaskinni.

  • I can describe the MVT components of my copy of Flaskinni.

  • I can describe the thread's steps as my app starts up.

Homework

Install

So, uhh, where do you want to work?

You can edit your code on the cloud with cool sites like SourceLair. A cloud-based IDE is likely going to be based on Linux. That means you'll need to know a bit about Linux CLI. Linux plays well with coders and a cloud-based IDE makes it easy to share your development progress with people and get feedback faster. That's big.

You can also setup right on your computer. You should know how to do this just so you can practice putting all the pieces together. I prefer to program in the cloud and locally and use GitHub to keep the two environments in sync.

SourceLair

At Gilmour, going to set you up with a pre-configured environment. You're pretty much all set. You'll need to create a private.py file with the database login details. You'll also need to create a file named .env with at least one line, DB_HOST=postgres

Your Machine

  1. Install Python

  2. Git and basic dev tools

    1. If you're on Windows: Git Bash

    2. If you're on Mac: Xcode and Homebrew

  3. PostgreSQL (remember your database username and password as you'll be needing this later. I use postgres / postgres in the demo code below)

  4. VS Code installed then add Python extensions

  5. Clone Flaskinni by pressing CTRL+SHIFT+P in Visual Studio Code and selecting git clone and enter:http://github.com/dadiletta/flaskinni

  6. Setup a virtual environment in the terminal of VS Code by pressing CTRL + ` and typing python3 -m venv venv

  7. Activate the virtual environment

    1. Mac: source venv/bin/activate

    2. Windows: source venv/Scripts/Activate

  8. Install your dependencies: pip install -r requirements.txt

    1. If uwsgi throws an error, remove those lines from requirements.txt and run pip install again. Those modules don't install on Windows machines.

  9. Create the private.py file using the example below

    1. Connect with a database. This is where Docker makes things so much easier. But if you've installed pgAdmin with PostgreSQL, you can use that and right click on "Databases" and create one called db-flaskinni

    2. Create a file called .env

The private.py file isn't auto generated and it's required for a Flaskinni project. We'll need to make it
private.py
.env
private.py
import os
SECRET_KEY = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
DEBUG = os.environ.get("DEBUG", True)
DB_USERNAME = os.environ.get("DB_USERNAME", 'postgres')
DB_PASSWORD = os.environ.get("DB_PASSWORD", 'postgres')
DATABASE_NAME = os.environ.get("DATABASE_NAME", 'flaskinni_db')
UPLOADED_IMAGES_DEST = os.environ.get("UPLOADED_IMAGES_DEST", \
'/mnt/project/app/static/images')
UPLOADED_IMAGES_URL = os.environ.get("UPLOADED_IMAGES_URL", '/static/images/')
# FLASK-MAIL CONFIG
MAIL_SERVER = 'smtp.gmail.com'
MAIL_PORT = 465
MAIL_USE_SSL = os.environ.get("MAIL_USE_SSL", True)
MAIL_USE_TLS = os.environ.get("MAIL_USE_TLS", False)
MAIL_USERNAME = os.environ.get("MAIL_USERNAME", 'flaskinni@gmail.com')
MAIL_PASSWORD = os.environ.get("MAIL_PASSWORD", 'xxxxxxxxxxx')
ADMIN_EMAIL = os.environ.get("ADMIN_EMAIL", 'flaskinni@gmail.com')
MAIL_DEFAULT_SENDER = 'flaskinni@gmail.com'
# STARTING ACCOUNTS
STARTING_ADMIN_PASS = 'flaskinni123'
STARTING_ADMINS = ['flaskinni@gmail.com', 'flaskinni2@gmail.com']
#Flask-Security
SECURITY_PASSWORD_HASH = 'pbkdf2_sha512'
SECURITY_PASSWORD_SALT = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
#SSL Verification
SSL_ROUTE = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
SSL_KEY = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
.env
FLASK_ENV=development
FLASK_APP=flaskinni
DB_HOST=0.0.0.0

Using Docker (incomplete)

I need to study up on Docker. These are notes that I'll use to redo this section later. It's important to try new stuff, even if you're not moving it into your stack. Docker represents a booming growth in containers, and that's something serious web dev folks need to deploy at scale.

  1. Make sure Docker is installed, running and logged in.

  2. Clone Flaskinni git clone http://github.com/dadiletta/flaskinni

  3. Create a private.py file inside the flask and a docker.env file

    docker.env ( POSTGRES_USER=ubuntu POSTGRES_PASSWORD=ubuntu POSTGRES_DB=db DB_HOST=db

  4. Optional: docker-compose up --build to test to see if Flaskinni will run before you change its name.

  5. Change name from flaskinni to your project’s title (lowercase)

    1. Change the name of the folder

    2. uwsgi.ini should be the same as the name of the folder

    3. docker-compose.yml should be changed to match the name of the new folder in the volume section. The container name should also be adjusted.

    4. Replace all python references to flaskinni to your new folder name

      1. manage.py (we no longer use manage.py)

      2. inni/views.py

      3. Dockerfile

  6. Security

    1. Change the name of the db in docker.env and match it in private.py

  7. docker-compose up --build

Start it Up

Assembling the App

Runner

With your (venv) activated, the flask run command will start flaskinni.py and that calls the app factory in the initialization file within our app folder.

App Factory & Blueprints

Flaskinni comes with the main blueprint. That'll affect your use of url_for. So if you wanted to make a link to the homepage, it'd be <a href="{{ url_for('main.index') }}">home</a>

Flaskinni comes with one blueprint. You'll probably want to add more soon
PrettyPrinted is an awesome channel and I recommend buying his courses

Your Flaskinni app is now working and you know what a blueprint is.

What if it's not working?

You're going to get stuck... a lot. This is where you can distinguish yourself as a programmer. Every problem big and small you hack your way through makes you more legit. So enjoy the grind. It's important to read around the problem and not just through it. Sloppy copying from StackOverflow once you copy and paste the error into Google can get you in trouble. Get background research done to understand some of the bigger concepts. Be willing to reach out for help, but respect that other people are grinding away too. Always show evidence of your research when asking for help as it will build trust with your support network.

Initialize alembic - DON'T FORGET!

Pity the poor programmers that forget to setup their migration tools. Changes to their database will be a big headache. You, however, will remember to punch in flask db init and we'll talk about why that's super important later.

Template

Now that Flaskinni is running in your environment, local or cloud-based, let's take a tour.

Base

The T part of our MVT all starts with base.html. See how much of the documentation you can read before your brain wimps out

The purpose of the base template is to give all the pages in your app common assets. This such as fonts, CSS files, nav bar. Every page must link to the base file. It is kept in the /templates folder, as base.html. The base template is like a picture frame, loading common elements like the nav bar, footer, and your little favicon.

I can't remember where this picture in my notes came from but it's not my creation.

This is the whole parent-child thing. The parent is the skeleton of your page. We named it base.html and it has <link>s to our Google Fonts, CSS files, and sets up nav bars and any other element that’s site-wide. It’s great! Pretty much sets up the whole theme of our site.

To put our pages inside this metaphorical picture frame, we use the command, {% extends "base.html" %} at the top of every other HTML we put in our templates folder.

The base template extends across all pages, so the website can share HTML code.

If you give this guide up and follow Mr. Grinberg's exclusively I won't judge.

Blocks

We’re going to make a new template that inherits from our base.html so you can experiment with this handy tool. Create a new file in your templates folder called about.html. This page will need hardly any information at all because all the setup is done in the parent. All we need to do is pass some HTML into any block.

index.html
index.html
{% extends "base.html" %}
{% block content %}
{# this is a comment #}
<div class="row justify-content-md-center">
<div class="col-md-6">
<h1>Hi</h1>
</div>
</div>
{% endblock %}

Sass

Sass makes CSS easier, even if you have to spend a few minutes here and there refreshing yourself on how to use the Sass tricks and how to compile your Sass down to CSS. I personally use a VSCode extension to manage this automatically.

Don't want to learn? There is a lazy way out and that's to edit your CSS directly. No judgements.

  1. Pick out a header and a body font at fonts.google.com

  2. Add them to your collection and get the <link> we’ll use to embed a connection to Google Fonts.

  3. Replace the other <link> to fonts.google.com that was in your base.html file

  4. Add your CSS code to custom.css, something like:

custom.css
custom.css
body {
font-family: 'Roboto Mono', monospace;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Rammetto One', cursive;
}
  1. Launch your server

    1. (make sure you’ve got venv activated) source venv/bin/activate or on Windows: source venv/Scripts/Activate

    2. (use our flask-script to run the server) flask run

  2. Check out your font changes!

Views

If you want to see the new template file in action, we’ve got to build a route to that page. As of this writing, there are only two locations for routes in Flaskinni, the master __init__.py file and the views.py file inside the blog module folder. Since our homepage route is in the first file, we’ll put our about route there, too.

# Created a new route used for rendering the about page template
# Custom app routes for the end of the website url
@app.route('/about')
@app.route('/about/')
@app.route('/about.html')
# Define app route
def about():
"""Render the template"""
return render_template('about.html')

We later passed a variable to the template. We dropped <h1> {{ some_variable }} </h1> in our about.html file. Nothing shows up until we change the render_template call to something like this:

def about():
"""Render the template"""
return render_template('about.html', some_variable="Hello!")

Congratulations! You just passed your first variable to a template.

url_for

Flash Notifications

We have cool, color changing notifications in Flaskinni that rely on Bootstrap classes. So if you want to send a good alert message that something worked, you'd say: flash("Your message has been sent!", "success")

But if something failed, you'd write:

flash("Your message was destroyed by mistake. Whoops!", "danger")

What other Flash notifications are there? How are the flash notifications being built? Let's look at where they come up in the base.html file, trace back the macro that builds the flash messages and then see what other options come with Flaskinni.

There we include the _message.html partial
Those are the options you've got. You can totally add your own.

Sessions

Models

Let's look over our models. Notice the two mains:

Connecting Routes with Models

Let's say I want to view my blog page where I see all my posts. If I wanted to build a link to this blog and the route below was in my main blueprint, I'd use something like <a href="{{ url_for('main.blog') }}">Check out my blog!</a>. Before Flask starts to render the HTML page showing my blog, it will first execute a database query to look up all the blog posts:

@app.route('/blog')
@app.route('/blog/')
def blog_index():
posts = Post.query.order_by(Post.publish_date.desc())
return render_template('blog/index.html', posts=posts)

After the posts are queried, they're passed to the template as a keyword argument in the render_template method. That will allow my template to loop through all the posts and build cool UI elements for each.

Command Line Interface

flask shell

This is a really useful skill we should go over as it helps us test and figure out where things go wrong.

flask shell

if python just said it doesn't know what that is, then you need this line: from extensions import db

from user.models import User

my_users = User.query.all()

look at your user(s): my_users my_users[0].email

my_users[0].first_name

Talking to Postgres

Let's make sure we can login directly to our database. You can skip this step if you're, say, in the midst of a hackathon, the app is working, and you've got to keep moving. In the long-run, it's critical you know how to flex control over your database directly.

If you're using Terminal, bash or PowerShell, you should be able to type psql to open a command-line version of PostgreSQL. You can also use a friendlier interface like pgAdmin, though I recommend making the effort to explore both. You'll need to specify your user when accessing psql. Specifying your user can vary a lot depending on how you approach it. Good thing you know how to Google.

Linux:

sudo service postgresql start psql (try listing the current databases) \l (if you need to quit) \q

If you’re the superuser using sudo -i then you’ll be on the root account (don’t do that). Postgres has a built-in admin you can log into with su - postgres and then psql (you shouldn’t need that but it’s useful).

You can edit accounts' psql access and credentials with stuff like:

ALTER ROLE ubuntu WITH PASSWORD 'ubuntu';

You probably won’t need this very often, but it’s important to know if you want to understand how your app works. Launch psql and be very careful as you tiptoe through our database:

(connect to our database, which in this example is named “db”) \c db

(check out the tables in our database) \dt

(see what we’ve got in our post table) SELECT * FROM post;

If there are no posts, we’re going to create the first one, which will have the ID of zero. If there are posts already, we’re going to create one with the next highest number.

(let’s add a new record of a post)

INSERT INTO post (id, user_id, title, subtitle, body, slug, live) VALUES (0, 0, “Some title”, “Some subtitle”, “Some body”, “someslug”, True);

(let’s quit psql) \q

Launch your app and check it out. Not pretty, right?

(go back into psql and connect to our db then let’s delete what we made)

DELETE FROM post WHERE id=0;

(make sure it’s gone) SELECT * FROM post;