2013-12-14

After a number of requests for a basic Flask and RethinkDB template, I decided to go ahead and write a blog post. This is that post.

BTW: We always welcome requests. If you have something you'd like us to write about, or build, shoot us an email.

Today we'll be creating a simple todo list, which you'll be able to modify to meet your own needs. Before beginning, I highly suggest reading over this article, which details how RethinkDB differs from some of the other NoSQL databases.

Set up RethinkDB

  1. Install

    Navigate here and download the appropriate package for your system. I used Homebrew - $ brew install rethinkdb - and it took almost twenty minutes to download and install the build:

    ==> Installing rethinkdb
    ==> Downloading http://download.rethinkdb.com/dist/rethinkdb-     1.11.2.tgz
    ######################################################################## 100.0%
    ==> ./configure --prefix=/usr/local/Cellar/rethinkdb/1.11.2 --  fetch v8 --fetch protobuf
    ==> make
    ==> make install-osx
    ==> Caveats
    To have launchd start rethinkdb at login:
      ln -sfv /usr/local/opt/rethinkdb/*.plist   ~/Library/LaunchAgents
    Then to load rethinkdb now:
      launchctl load   ~/Library/LaunchAgents/homebrew.mxcl.rethinkdb.plist
    ==> Summary
    ๐Ÿบ  /usr/local/Cellar/rethinkdb/1.11.2: 174 files, 29M, built in   19.7 minutes
    
  2. Install the Python drivers globally:

    $ sudo pip install rethinkdb
    

    Note: I installed Rethink globally (outside of a virtualenv) since I'll probably use the same version with a number of projects, with a number of different languages. We will be installing in within a virtualenv later on in this tutorial.

  3. Test

    First, let's start the server with the following command:

    $ rethinkdb
    

    If all is installed correctly, you should see something similar to:

    info: Creating directory /Users/michaelherman/rethinkdb_data
    info: Creating a default database for your convenience. (This is because you ran 'rethinkdb' without 'create', 'serve', or '--join', and the directory '/Users/michaelherman/rethinkdb_data' did not already exist.)
    info: Running rethinkdb 1.11.2 (CLANG 4.2 (clang-425.0.28))...
    info: Running on Darwin 12.4.0 x86_64
    info: Loading data from directory    /Users/michaelherman/rethinkdb_data
    info: Listening for intracluster connections on port 29015
    info: Listening for client driver connections on port 28015
    info: Listening for administrative HTTP connections on port 8080
    info: Listening on addresses: 127.0.0.1, ::1
    info: To fully expose RethinkDB on the network, bind to all addresses
    info: by running rethinkdb with the `--bind all` command line option.
    info: Server ready
    

    Then test the connection. Open a new window in your terminal and enter the following commands:

    $ python
    >>> import rethinkdb
    >>> rethinkdb.connect('localhost', 28015).repl()
    

    You should see:

    <rethinkdb.net.Connection object at 0x101122410>
    

    Exit the Python shell but leave the RethinkDB server running in the other terminal window.

Set up a Basic Flask project

  1. Create a directory to store your project:

    $ mkdir flask-rethink
    $ cd flask-rethink
    
  2. Set up and activate a virtualenv:

    $ virtualenv --no-site-packages env
    $ source env/bin/activate
    
  3. Install Flask and Flask-WTF:

    $ pip install flask
    $ pip install flask-wtf
    
  4. Create a requirements file to store all of your project's dependencies:

    $ pip freeze > requirements.txt
    
  5. Download the Flask boilerplate, which can be found in the template directory of this repo.

  6. Your project structure should now look like this:

    .
    โ”œโ”€โ”€ app
    โ”‚ย ย  โ”œโ”€โ”€ __init__.py
    โ”‚ย ย  โ”œโ”€โ”€ forms.py
    โ”‚ย ย  โ”œโ”€โ”€ models.py
    โ”‚ย ย  โ”œโ”€โ”€ templates
    โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ base.html
    โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ index.html
    โ”‚ย ย  โ””โ”€โ”€ views.py
    โ”œโ”€โ”€ readme.md
    โ”œโ”€โ”€ requirements.txt
    โ””โ”€โ”€ run.py
    
  7. Run the app:

    $ python run.py
    
  8. Navigate to http://localhost:5000/, and you should see:

main

Don't try to submit anything yet, because we need to get a database setup first. Let's get RethinkDB going.

RethinkDB Config

  1. Install RethinkDB in your virtualenv:

    pip install rethinkdb
    
  2. Add the following code to "views.py":

    # rethink imports
    import rethinkdb as r
    from rethinkdb.errors import RqlRuntimeError, RqlDriverError
    # rethink config
    RDB_HOST =  'localhost'
    RDB_PORT = 28015
    TODO_DB = 'todo'
    # db setup; only run once
    def dbSetup():
      connection = r.connect(host=RDB_HOST, port=RDB_PORT)
      try:
          r.db_create(TODO_DB).run(connection)
          r.db(TODO_DB).table_create('todos').run(connection)
          print 'Database setup completed'
      except RqlRuntimeError:
          print 'Database already exists.'
      finally:
          connection.close()
    dbSetup()
    # open connection before each request
    @app.before_request
    def before_request():
      try:
          g.rdb_conn = r.connect(host=RDB_HOST, port=RDB_PORT, db=TODO_DB)
      except RqlDriverError:
          abort(503, "Database connection could be established.")
    # close the connection after each request
    @app.teardown_request
    def teardown_request(exception):
      try:
          g.rdb_conn.close()
      except AttributeError:
          pass
    

    Check the comments for a brief explanation on what each of these functions do.

  3. Start your server again. You should see the following alert in your terminal:

    Database setup completed
    

    If you see this error rethinkdb.errors.RqlDriverError: Could not connect to localhost:28015. your RethinkDB server is not running. Open up a new terminal window and run $ rethinkdb.

    So, we created a new database called "todo", which has a table called "todos".

    You can verify this in the RethinkDB Admin. Navigate to http://localhost:8080/. The admin should load. If you click "Tables", you should see the database and table we created:

    rethink-admin

  4. Display Todos

    With the database setup, let's add code to display todos. Update the index() function in "views.py":

    @app.route("/")
    def index():
      form = TaskForm()
      selection = list(r.table('todos').run(g.rdb_conn))
      return render_template('index.html', form=form, tasks=selection)
    

    Here we're selecting the "todos" table, pulling all of the data, which is in JSON, and passing the entire table to the template.

  5. Add data manually

    Before we can view any todos, we need to add some first. Let's go through the shell and add them in manually.

    $ python
    >>> import rethinkdb
    >>> conn = rethinkdb.connect(db='todo')
    >>> rethinkdb.table('todos').insert({'name':'sail to the moon'}).run(conn)
    {u'errors': 0, u'deleted': 0, u'generated_keys': [u'c5562325-c5a1-4a78-8232-c0de4f500aff'], u'unchanged': 0, u'skipped': 0, u'replaced': 0, u'inserted': 1}
    >>> rethinkdb.table('todos').insert({'name':'jump in the ocean'}).run(conn)
    {u'errors': 0, u'deleted': 0, u'generated_keys': [u'0a3e3658-4513-48cb-bc68-5af247269ee4'], u'unchanged': 0, u'skipped': 0, u'replaced': 0, u'inserted': 1}
    >>> rethinkdb.table('todos').insert({'name':'think of another todo'}).run(conn)
    {u'errors': 0, u'deleted': 0, u'generated_keys': [u'b154a036-3c3b-47f4-89ec-cb9f4eff5f5a'], u'unchanged': 0, u'skipped': 0, u'replaced': 0, u'inserted': 1}
    >>>
    

    So, we connected to the database, then entered three new objects into the table within the database. Check the API docs for more information.

    Fire up the server. You should now see the three tasks:

    tasks

  6. Finalize the form

    Update the index() function again to pull the data from the form and add it to the database:

    @app.route('/', methods = ['GET', 'POST'])
    def index():
      form = TaskForm()
          if form.validate_on_submit(): 
              r.table('todos').insert({"name":form.label.data}).run(g.rdb_conn)
              return redirect(url_for('index'))
          selection = list(r.table('todos').run(g.rdb_conn))
          return render_template('index.html', form = form, tasks = selection)
    

    Test this out. Add some todos. Go crazy.

Challenges

The current app is functional, but there's a lot more we can do. Take this app to the next level.

Here's a few ideas:

  1. Add a user login.
  2. Create a more robust form, where you can add a due date for each todo, and then sort the todos by that date before rendering them to the DOM.
  3. Add functional and unit tests.
  4. Add the ability to create sub tasks for each task.
  5. Read over the API reference docs. Play around with various methods.
  6. Moduralize the app.
  7. Refactor the code. Show off your new code to RethinkDB.

What else would you like to see? Interested in seeing a part 2? How do you like RethinkDB in comparison to MongoDB? Share your thoughts below.

You can grab all the code from the repo. Cheers!



Want to learn more? Download the Real Python course.

Download Now ยป $60

Or, click here to learn more about the course.



blog comments powered by Disqus