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
- Set up a basic project like our previous projects.
- 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 oftext
- Password, with a name of
password
and a type ofpassword
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 aPOST
, 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:
- A text field for the blog post's title.
- A
textarea
to write the blog post itself. - 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.