fbpart2

Building a Flask Blog: Part2



In this part of the Flask-Blog we will create all the basic blog features like displaying the posts, querying by category and author name and we will mould our relationships to a bit to make it more complete.

If you have followed the previous article correctly, you will have the directory structure and all the stubs setup correctly with pretty decent authentication system but it still lacks a lot of things to call it a complete authentication system. If you have joined in this part, no worries, you can quickly go through the first part and can start working with this part immediately.

Till now, you can just create the articles by signing in and we have no track of who created this article and who signed up and we cant see the full articles too. lets start fixing from beginning one by one.

NOTE: There are no mentions of imports in the code snippet, if you want to know the imports, refer the last section of this article.

Displaying and Updating Articles

Open up your views.py and lets write the route for displaying full articles.

We have declared the route and our route takes the id and slug as the values along with the static article named route. Dynamic values in Flask are rendered using the < > brackets and since id and slug will be dynamic for each post, we are declaring them inside angle brackets and the default type of dynamic values is string and thus we have to declare the type of id specifically else id will be considered as the string value by the Flask. Your next question will be, how does this route function ? Well, suppose your URL has the structure like the defined route i.e http://localhost:5000/article/2/new-post , then Flask will automatically find the matching route and since we have declared the similar route, id will be taken as 2 and slug will be taken as new-post and the values will be passed inside our view for further process.

2a

 

Then we define our function and it takes the id and slug as the argument,here we are using find_by_id method to find the articles with the id as the argument and in our case Flask will find the article with id=2 and then return the dict value to the template with the article we filtered from the database.

We dont have find_by_id method method yet and show_article template also, lets create them now.

Open models.py and declare another @classmathod called as find_by_id to Article model which will filter the articles with the id provided to us from the URL the user passes.

This method will take the id passed by user as the arguement and will filter the Article model based upon the id value passed and then return the article. This method is straight forward.

Add the slug @property to the Article model

Create show_article.html inside the templates directory.

Next, lets change our index.html to reflect our changes.

We have changed the title with the anchor tag and the href pointing towards the show_article method with the id and slug derived from the article’s title to get the desired url pattern we declared in our route. Now run the server and you will notice that now you can display full articles.

Now lets add method to edit the articles.

You will notice the routing for this method is also very similar to the previous one, that’s because we need to get the id of the post to update the article in the record and we are performing both GET and POST request on this URL and we are getting the article based upon the id passed and if no article is found return the custom created HTTPNotFound method else get the ArticleUpdateForm along with the contents from the queried article, if the form validates after adding new content, add the article into session and commit the changes and then redirect to index. If form doesn’t validate, render the create.html to create the new article.

Lets create our custom 404 method and ArticleUpdateForm.

We are using the errorhandler method to generate the 404 pages, and when exception comes, error.html will be rendered automatically.

error.html

ArticleUpdateForm

You can see that ArticleUpdateForm inherits from the ArticleCreateForm with the hidden id, this id will be passed to render the ArticleUpdateForm for us.
Lets change the ArticleCreateForm to make ArticleUpdateForm work.

We have used the query_string method to get the parameters to work with. query_string uses URL parameters to change them to raw bytestring.

Lets change show_article.html to reflect our article_update method.

With that we can now edit the articles.
2b

2b

 

Now you can successfully create the article, view the article and edit the article. Lets style our Blog a bit.
We are going to add the TinyMCE to our body to format the article better.
Download TinyMCE and include the tinymce.min.js inside your static folder.
2b

After including TinyMCE add this script to your <head> </head> in layout.html file.

Add this to your custom.css file

Now when you signin, you will have pretty sophisticated WYSIWYG editor to format your body.
2b

When you create articles now you must have got the unformatted output. To fix that update the article.body like this where ever you need to display the formatted output. In our case, you need to change the show_article.html and index.html.

We are passing the safe filter to our body to format the output.

When everything seems to work smooth there is a big loop hole in our authentication system, you must have noticed that without even signing in you can edit all articles. lets fix that first.

Change your show_article.html like this

Now, to edit the article, you have to be logged in and then only the session will be created and you can edit articles.

Modeling Relationships

Without Categories, blog will not be complete, lets create categories for our articles, to do so first we need to create the Category model to hold the data.

Now apply the python manage.py db migrate and python manage.py db upgrade to update the tables inside our database. Now Each Article will belong to one Category and one Category will have many Articles.
category => has_many articles and article => belongs_to one category
In order to design this relationship, we are going to use the db.relationship and Foreignkey to interlink the Articles Model and Category Model.

Update the Article Model to hold this relationship.

You can see that we have included the new column called category_name which will point to the category of the Article and then using the relationship key we are bridging this connection. Now when you migrate you can see the category_name column included with the Article Model in the database.

Next, create the CategoryCreateForm inside your forms.py

Since you have linked the Article Model with this Category, you need the Category column in the ArticleCreateForm to hold the Category value. We will be linking the Category dynamically by the WTForms QuerySelectField, which will query the Category Model and display all the Categories and it will be rendered in the ArticleCreateForm to choose from.

To do so modify your ArticleCreateForm like this

We are querying the Category and then by using QuerySelectField we are displaying the queried results.
Next declare the category method inside your views.py

Next create the cat_create.html inside your templates.

Now change your create.html to include the Category fields.

2b

Now move on to create a new category and see what happens. I have created two categories and now when the page redirects, you will see that the category field is automatically populated with the two categories you created.
2b

Next, Create a new article by choosing any of the categories. I have created 3 new post with both python and flask category.
2b

You can see that category column is populated with the category we specified, we can edit the article and change the category also.

Until Now everything works fine, but we still have many loop holes like we dont have control who can edit the articles and who created the articles. To achieve this lets reshape our Article Model.

Its exactly similiar to what we did to bridge categories with articles, we have created a person_name to be in relationship with the Article and thus one person can have many articles but they all belong to only one person. Now again migrate the changes.

Once again we will change the ArticleCreateForm to get the data who created the Article but not like what we did for category. We are going to use the session['key'] as the variable to play around.

Here we have declared the person_name as the hidden field because we don’t want to change the name of author who created since we will get the person name from the session key i.e when the user logs in, session key will be setup with his email id and we will query the database using that email id and we will display the person name. We will be using this to protect the articles from being edited by the anonymous member also. Based upon the session email id, the articles will be open for editing and the other articles will be locked down for that person.

In short, when we sign in with different accounts, we can still edit all the articles which is not good and so we will create a system in which the person, who has authored the article can edit only his articles not anybody’s else. You will have better understanding when we do this in action.
First of all change the profile method in views.py to make use of the person_name hidden key.

Here we are seeing if the email is in session i.e if the person has logged in. We are querying the person table and filtering out the person who has logged in with the help of session key which in our case is the ‘email’.
For e.g
Suppose john@doe.com has logged in, then this email will be in session and we are querying the database for this email id and storing the john@doe.com details in the person object and then we are using the hidden key from the ArticleCreateForm and setting the value of person_name field in the database by the person’s name i.e john@doe.com ‘s name which the person used during signup.

Update the create.html to include hidden field.

Now, when the person creates a new article, his name will be stored in the database.When i logged in as john@doe.com, the firstname i.e John gets stored in the database alongside the article which John created.2b

If you create more articles with different logins, you will realize that anyone can edit the article, which is really redundant, so now lets fix that.
Update show_article like this

Here, we are first querying the article by id and then seeing the person who has logged in and storing it in person object and then we are querying the person_name associated with the article and storing in the person_name and passing both the dict value to the template such that we can compare the results in the template directly.
Edit show_article.html like this

Now we are checking whether the name of logged in person and the name of person who created the article are the same, if they are same, then render the Edit Article url. Now the other users cannot edit the article, only the person who created the article can edit the article.

2b

 

I am logged in as doe@john.com and i am not able to edit the article and if you are too clever to get the url for edit page manually, it will be redirected to HTTPNotFound unfortunately.

Displaying Author and Category Articles

its pretty easy to display the article filed under particular category or author.

change your models.py file to include the respective @classmethods.

Now include this in your view.py.

The template for author and category should be like author.html

category_view.html

Cleaning up

change your index.html to make a link to the author and category pages.

Completed Files

view.py

models.py

forms.py

Conclusion

This completes our Part 2 in the Flask-Blog series. Code refactorings are welcomed. Meet you soon in Part 3.

Source Code

Download it from Github.

 


  • Yuanle

    Thanks!