Monday 28 October 2013

A basic web app using Flask and MongoDB

This tutorial will show you how to set up a very simple database web application using the micro-framework Flask. We'll be using the Python library pymongo, which is available on PyPI and installable through PIP, and the free sandbox database at Mongolab. We'll use git with bitbucket to deploy the code to the server.

I assume that you already have root access to an Ubuntu server. This example was tried on a newly created Ubuntu 12.10 64-bit "Droplet" on Digital Ocean.

Included are very basic instructions to install Apache2 and mod-wsgi, too, but the emphasis of this tutorial is simplicity instead of security, so if you want instructions on setting up Apache2, read a tutorial specifically about that as well.

Let's begin with installing a few things we'll need. I've listed them all on separate lines, as you may have some of them already, and some of them are not absolutely necessary.

sudo apt-get install apache2 libapache2-mod-wsgi
sudo apt-get install libpcre3-dev
sudo apt-get install python-pip
sudo apt-get install build-essential python-dev
sudo apt-get install git-core

Apache2 is the web server we'll be using to serve our app, and libapache2-mod-wsgi is the wsgi mod for Apache which will let us use Python as a backend for a webpage. We'll use pip, a python package manager, to install Flask, which will allow us to easily interface with wsgi, and pymongo, which we'll use to connect to a Mongo database.

Note that the libpcre3-dev install is not strictly necessary, but pip will complain that it was unable to install the Flask C extensions necessary for speedups if you do not have it. Similarly, the build-essential and python-dev installs are not necessary, but are required for the C extension speedups for pymongo.

Now the python libraries we need

sudo pip install flask
sudo pip install pymongo

Make sure that you have pip, Flask, Pymongo, and git installed on your local machine as well. If you're using Ubuntu, follow the same steps above. I created this demo using Windows 7 as a local machine, and the tools are all available for Windows as well.

Next, we want to create a Mongo database. This is most easily done through Mongolab, which offers a free option. Head to mongolab.com, and create an account.

Create a new database, (for host options, the default AWS is probably the best option. Just select the location closest to your digitalocean server. As mine is in Amsterdam, I chose Ireland). Give it a name. Add a new user for the database and remember the username and password. Take note of the five pieces of information you need, given to you in the form of a url. See the screenshots below for a step-by-step guide on doing this.

Create a new database

Choose the free "development" server

Name your new database and press Create

Click on the database in the list to configure it

Click add a new user

Choose a database username and password.
(Note that this is different from your MongoLab username and password)

Make a note of the five bits of information we'll need in the python script.

Now create a new directory tree on your local machine with the following structure

demo
    \templates
    index.py
    index.wsgi

In index.py, type or copy the following, changing the database info in the connect() method as relevant.

from flask import Flask, render_template, request, redirect
import os
from pymongo import MongoClient

def connect():
# Substitute the 5 pieces of information you got when creating
# the Mongo DB Database (underlined in red in the screenshots)
# Obviously, do not store your password as plaintext in practice
    connection = MongoClient("ds053128.mongolab.com",53128)
    handle = connection["demo"]
    handle.authenticate("demo-user","12345678")
    return handle

app = Flask(__name__)
handle = connect()

# Bind our index page to both www.domain.com/ 
and www.domain.com/index
@app.route("/index" ,methods=['GET'])
@app.route("/", methods=['GET'])
def index():
    userinputs = [x for x in handle.mycollection.find()]
    return render_template('index.html', userinputs=userinputs)

@app.route("/write", methods=['POST'])
def write():
    userinput = request.form.get("userinput")
    oid = handle.mycollection.insert({"message":userinput})
    return redirect ("/")

@app.route("/deleteall", methods=['GET'])
def deleteall():
    handle.mycollection.remove()
    return redirect ("/")

# Remove the "debug=True" for production
if __name__ == '__main__':
    # Bind to PORT if defined, otherwise default to 5000.
    port = int(os.environ.get('PORT', 5000))

    app.run(host='0.0.0.0', port=port, debug=True)

Open index.wsgi and add:

import sys
sys.path.insert(0, '/var/www/demo')
from index import app as application


In the templates directory, create a new file called index.html. Add the following code: 

<html>
<head>
<title>Demo</title>
</head>
<body>
<h1>Hello</h1>
<form action="/write" method="POST">
<input type="text" name="userinput" id="userinput">
<input type="submit" value="Submit">
</form>
<a href="./deleteall">Delete all</a>

{% for userinput in userinputs %}
<p>'{{userinput.message}}' has database id {{userinput._id}}</p>
{% endfor %}
</body>

</html>

Try this out by running python index.py from the demo directory (or just index.py if you're in Windows.) Then open a web browser and navigate to http://locahost:5000

You should see a page like this:


The demo web app


You can submit text through the html form, and the results will appear below, displaying the Mongo Database _id associated with that entry.

Great - now head over to bitbucket.org and sign in or create an account. Press "Create" and create a new Git repo called demo. Now go to your demo directory in Terminal or Command Prompt and type (substituting your bitbucket username):

git init
git remote add origin https://sixhobbits@bitbucket.com/sixhobbits/demo.git
git add .
git commit -m "initial commit"
git push origin master
[enter your bitbucket password when prompted]

On your server, navigate to /var/www and type (again substituting your bitbucket name)

sudo git clone https://sixhobbits@bitbucket.com/sixhobbits/demo.git
[enter your bitbucket password when prompted]

And your code is ready. Now we just need to tell Apache where to find it. type
cd /etc/apache2/sites-available
ls

If you've just installed Apache you'll see a default file (and possibly a default-ssl one too). Type:

sudo a2dissite default

To disable the default site. Then type (substituting your domainname in place of flaskmongodemo.tk. You can get a .tk domain for free - just head over to dot.tk, find one that hasn't been taken yet, and point it to your digital ocean server's IP address).

sudo nano flaskmongodemo.tk

And paste the following code (I use vim for this step, but nano is installed by default on Ubuntu). Substitute your domain name in place of flaskmongodemo.tk.

<VirtualHost *:80>
    ServerName www.flaskmongodemo.tk
    ServerAlias flaskmongodemo.tk


    WSGIDaemonProcess www.flaskmongodemo.tk
    WSGIScriptAlias / /var/www/demo/index.wsgi


    <Directory /var/www/demo/index>
        WSGIProcessGroup index
        WSGIApplicationGroup %{GLOBAL}
        Order deny,allow
        Allow from all
    </Directory>

</VirtualHost>

Save your file (in Nano Ctrl + X and then Y when prompted) and type (again substituting your domain name)

sudo a2ensite flaskmongodemo.tk
sudo service apache2 reload

And it's done. You should be able to navigate to your url and see your app in action! Now that you can write to and read from a database, the possibilities are endless.

A useful command if things don't go as expected and you see an error instead of your app:

tail -f /var/log/apache2/error.log

This will show the error log of Apache, which is often helpful in tracing down cause of a problem.

The full app is currently at bitbucket.org/sixhobbits/demo.git, so you can simply clone it directly from there if you want.

3 comments:

  1. the headlines application in your book until page 28 gives 500 error. I followed everything you carefully and for days I still give the same error in production. On my local computer is perfect. Any ideas?

    ReplyDelete
    Replies
    1. Hi Lawrence,
      You'll need to look in /var/log/apache2/error.log on the server and see what error its giving. It might be an issue with Linux users where you're installing the necessary libraries through pip as your main user but serving the web app using a different user. I would suggest you post a detailed description of problem on Stack Overflow. Feel free to send me the link when you've done so. My contact details are on dwyer.co.za

      Delete