Assignment 2

This assignment is to write a simple blogging application in Flask. It is due on March 22.

Attention

This is a solo assignment, so you must complete and submit it on your own. You may talk about general concepts with your classmates, but all code you submit must be your own or adapted from the class examples.

Before completing this assignment, make sure that you can set up and run an assignment as described in Assignment 0.

This assignment will exercise the following skills:

  • Flask programming
  • Handling data from forms
  • Storing data for future retrieval
  • Managing user authentication and authorization
  • HTML escaping
  • CSS styling

Note

This assignment provides less detailed instruction on exactly what URLs should look like. I'm happy to talk about ideas for them, but as we progress you should be growing in your ability to develop such things yourself.

Part 1: Basic Structure

  1. Set up a basic project like our previous projects.
  2. Install the Markdown package in your virtual environment (pip install Markdown).

settings.py

Flask allows applications to load configuration settings from a separate file. This is useful because many settings, such as passwords, should not be included in your main application source file; they should not even be committed to Git.

Create a file settings.py with the following code:

SECRET_KEY = '<key>'
ADMIN_PASSWORD = '<password>'

<key> should be a randomly-generated string that is 12–32 characters long; you can use this search to generate such a thing.

<password> is the password you will use to log in to the blog application.

Then, add the following code to your main application to load it:

app.config.from_pyfile('settings.py')

This should be right below the line that creates the app object in the first place.

Part 2: Logging In

We don't want to just let anyone log in to our application, but we'll also need content before we can actually show anything. So let's start with logging in.

The main page for your application should have a Log In link, that takes the user to a log-in page.

This page should have a form with two fields:

  • User name, with a name of user and a type of text
  • Password, with a name of password and a type of password

Along with a submit button called Log In. The form should use the POST method (login should always be done via POST).

When the user logs in, the form handler should do the following:

  • Check that the user is ‘admin’.
  • Check that the password matches the admin password from the settings. You can access this setting as app.config['ADMIN_PASSWORD']. Just check that the two strings are equal.

If both of these conditions are true, then mark the user as authenticated (you can set flask.session['auth_user'] = 'admin' to do this), and redirect to the main page.

If either is false, show the login page and arrange that it shows an error message ‘Invalid user name or password’ above the form. This message should be styled so that it looks like an error (e.g. some red borders or text). This can be done in one of two ways:

  • Display the page directly in response to the POST request. In general, we always want to redirect in response to a POST, but displaying an error is an acceptable exception to this rule.
  • Redirect to the login page, but first store the error message in Flask's flashing system. You'll need to make your templates render flashed messages.

Note

This is not a secure way to handle user authentication. We will study a better way for P3.

Part 3: Being Different when Logged In

When the user is logged in, then the main page should not display ‘Log In’. Instead, it should display:

  • The text ‘Logged in as admin’
  • A link to ‘Log out’. This should go to a URL that, when visited, deletes the auth_user field from the session (thereby logging the user out) and redirects to the main page.
  • A link to post a new blog post. This should go to a page with a form to create a new blog post. More on that in a bit.

Part 4: Posting Blog Posts

Create a URL that displays a form for creating a new blog post. The form should have:

  1. A text field for the blog post's title.
  2. A textarea to write the blog post itself.
  3. A button to submit the form.

When the user clicks the submit button, the data should be sent in a POST request to the server.

In response to the POST request, then server should save the blog post in a list, like we did in the animals example.

Once the post is added, redirect to the post's post page (e.g. /posts/2).

Part 5: Displaying Posts

Create a URL handler and template that displays blog posts after they are created (see the previous part for creating them).

This template should display the blog post's title in an h1, and also in the page title. It should also display the page content. The title and content should be in an article element, with the h1 being the first child of article.

Before displaying the page content, process it for Markdown markup so that authors can write with style. If your blog post content is stored in a content attribute of blog objects, you can do this:

content = Markup(markdown.markdown(blog['content'], output_format='html5'))
return flask.render_template(, content=content)

You will need these imports:

import markdown
from markupsafe import Markup

The markdown.markdown function converts text with Markdown syntax into HTML. The Markup class tells Jinja that we have deliberately created HTML, so it doesn't escape it and show the HTML tags to the user.

Part 6: Blog Index

The main page should display the blog posts. It should have an h1 and page title that say ‘Recent Posts’ or something like that.

Each page should be in its own article, with its title still in an h1.

Write CSS code so that when an article is in the blog index, its headers look smaller. That is, an h1 in an article in the index page should like an h2 normally does.

Hint

A good way to do this is to wrap the whole index in a div with some class, such as blog-index. You can then style h1 elements that appear in articles on the index page:

###css
.blog-index article h1 {
    /* styling here */
}

Part 7: Saving Data

Like you did in P2, save the blog posts to a JSON file when new ones are added, and if the file exists when the application starts, load it.

Extra Credit: Pagination

Display the blog posts in pages of 5. If there are more than 5 blog posts, on the first page display a ‘Next Page’ link; on the second page, display a ‘Previous Page’ link (and another ‘Next Page’ if there are more than 10 posts), and so forth.