A newer version of this documentation is available.

View Latest

Setting up the project

Set up your directory layout and add some views in the server before you start dealing with the Python SDK and Flask itself.

Directory Setup

Create a project directory named beer:

$ mkdir beer
$ cd beer
$ mkdir templates
$ mkdir templates/beer
$ mkdir templates/brewery
$ mkdir static
$ mkdir static/js
$ mkdir static/css

Showing your directory contents displays something like this:

$ find . -type d
./static
./static/js
./static/css
./templates
./templates/brewery
./templates/beer

To make the application look pretty, we’re incorporating jQuery and Twitter Bootstrap. You can either download the libraries and put them in their appropriate css and js directories (under static ), or clone the project repository and use it from there. If you followed the quick start steps, you already have the files in your beersample-python directory. Either way, make sure you have the following files in place:

  • static/css/beersample.css

  • static/css/bootstrap.min.css (the minified twitter bootstrap library)

  • static/css/bootstrap-responsive.min.css (the minified responsive layout classes from bootstrap)

  • static/js/beersample.js

  • static/js/jquery.min.js (the jQuery javascript library)

From here on, you should have a bare-bones web application configured that has all the dependencies included. We’ll now move on and configure the beer-sample bucket the way we need it.

Setting up the views

The beer-sample bucket comes with a small set of predefined views, but to make our application function correctly we need some more. This is also a good chance to explore the view management possibilities inside the Web-UI.

Because we want to list beers and breweries by name, we need to define one view for each. Head over to the Web-UI and click on the Views menu. Select beer-sample from the drop-down list to switch to the correct bucket. Now click on Development Views and then Create Development View to define your first view. You need to give it the name of both the design document and the actual view. Insert the following names:

  • Design Document Name: _design/dev_beer

  • View Name: by_name

The next step is to define the map and (optional) reduce functions. In our examples, we won’t use the reduce functions at all but you can play around and see what happens. Insert the following map function (that’s JavaScript) and click Save.

function (doc, meta) {
  if(doc.type && doc.type == "beer") {
    emit(doc.name, null);
  }
}

Every map function takes the full document ( doc ) and its associated metadata ( meta ) as the arguments. You are then free to inspect this data and emit a result when you want to have it in your index. In our case, we emit the name of the beer ( doc.name ) when the document both has a type field and the type is beer. We don’t need to emit a value — that’s why we are using null here. It’s always advisable to keep the index as small as possible. Resist the urge to include the full document through emit(meta.id, doc), because it will increase the size of your view indexes. If you need to access the full document (or large parts), then use include_docs in the query method, which returns ViewRow objects together with their documents. You can also call cb.get(row.docid) to get the individual doc for a single row. The resulting retrieval of the document might be slightly out of sync with your view, but it will be fast and efficient.

Now we need to define a view for our breweries. You already know how to do this — here is all the information you need to create a brewery view:

  • Design Document Name: _design/dev_brewery

  • View Name: by_name

  • Map Function:

    function (doc, meta) {
        if(doc.type && doc.type == "brewery") {
            emit(doc.name, null);
        }
    }

    The final step is to push the design documents in production. While the design documents are in development, the index is applied only on the local node. Because we want to have the index on the whole dataset, click the Publish button on both design documents (and accept any pop-up windows that warn you about overriding the old design documents).

Flask application skeleton

We’ll be showing bits and pieces of the web app as it pertains to specific sections. The entire file is less than 300 lines long, and you can inspect it by looking into the beer.py file in the repository. First, our imports. We need some extra imports to be able to handle exceptions properly and let us build better view queries.

beer.py (imports)
from collections import namedtuple
import json

from Flask import Flask, request, redirect, abort, render_template

from couchbase.bucket import Bucket
from couchbase.exceptions import KeyExistsError, NotFoundError
from couchbase.views.iterator import RowProcessor
from couchbase.views.params import UNSPEC, Query

Then, we want to set some constants for our application:

beer.py (configuration)
CONNSTR = 'couchbase://localhost/beer-sample'
ENTRIES_PER_PAGE = 30

The ENTRIES_PER_PAGE variable is used later on to configure how many beers and breweries to show in the search results. Now, we’re ready to create our Flask application instance:

beer.py (creating the application)
app = Flask(__name__, static_url_path='')
app.config.from_object(__name__)

The first line creates a new Flask application. The first argument is the module in which the application is defined. Because we’re using only a single file as our application, we can use name, which expands to the name of the current file being executed (minus the .py suffix). The second argument instructs Flask to treat unrouted URLs as being requests for files located in the static directory we created earlier. This allows our templates to load the required .js and .css files. The second line creates a configuration object for our app. The argument is the name of the module to scan for configuration directives. Flask scans this module for variable names in UPPER_CASE and places them in the app.config dictionary.

Finally, let’s define a function to give us a database connection

beer.py (creating the connection)
def connect_db():
    return Bucket(app.config['CONNSTR'])

# Declare a global
db = connect_db()

You already know how to connect to a Couchbase cluster, so we’ll skip the explanation here.The module-level db variable is set to be the Connection object. In larger applications this is not a good idea, but we can get away with it here because this is a simple app.