Flask-Diamond

Flask-Diamond is a batteries-included Flask framework. Easily scaffold a working application with sensible defaults, then override the defaults to customize it for your goals.

Overview

Flask-Diamond imports many other Flask extensions and glues them all together. The end result is a model administration view, accounts and high-level account operations (e.g. password reset), testing, documentation, deployment, and more.

Installation

mkvirtualenv Flask-Diamond
pip install Flask-Diamond

Usage

workon Flask-Diamond
diamond-scaffold.sh ~/Documents/new-project
cd ~/Documents/new-project
mkvirtualenv -a . new-project
make install docs test db server

Introduction

Quick Start

Flask-Diamond installs in a Python environment with virtualenv. Please see the System Requirements for information about installation pre-requisites.

Install Flask-Diamond

Create a virtualenv for your new Flask-Diamond application, and then install Flask-Diamond with pip.

mkvirtualenv Flask-Diamond
pip install Flask-Diamond

Scaffold a new Flask-Diamond application

Create a directory to hold your application, and then scaffold a new Flask-Diamond application inside that directory. For this Quick Start, just use the default options.

workon Flask-Diamond
diamond-scaffold.sh ~/Documents/new-project
cd ~/Documents/new-project
mkvirtualenv -a . new-project

Use it!

Create the database:

make db

Start the server:

make server

You now have a server running at http://127.0.0.1:5000/admin. Visit your new application in a web browser and login with the following account details:

  • username: admin
  • password: aaa

Next Steps

It’s easy to scaffold a Flask-Diamond application. Now you can Learn Flask-Diamond by reading the online materials.

Learn Flask-Diamond

The best way to learn Flask-Diamond is to read the Developer Guide; contained within is a growing list of documents that explain various aspects of the Flask-Diamond approach. To help new learners focus on the fundamentals, read the following documents first.

  • Quick Start shows you how to scaffold a new application. You can be up and running in just a few minutes.
  • Project Initialization and Scaffolding offers many options for you to customize your application. Read this document to learn more about answering the questions during scaffolding.
  • Model-View-Controller with Flask-Diamond describes the architecture provided by Flask-Diamond. Model-View-Controller (MVC) is widely used in software engineering to write applications that provide a user interface. Once you understand how to implement MVC using Flask-Diamond, you will be able to write applications for a wide range of domains.
  • Writing an Application with Flask-Diamond provides examples and describes an approach for designing and programming something that achieves your goals.

Learning Python

If you are relatively new to Python, you may find that there are advanced computer science principles that Flask-Diamond requires some knowledge of. In particular, Flask-Diamond applications are heavily Object Oriented and make extensive use of Inheritance.

There are many tutorials online that can help you to learn about these fundamentals. In particular, I recommend the following:

System Requirements

Flask-Diamond requires some software to be installed in order to function. Once you have installed these requirements, you can follow the Quick Start to start your first project. The following packages should be installed globally, as the superuser, for all users on the system to access.

The following sections describe the process for installing these requirements on various systems. In each of the following examples, it is assumed you will be using a root account (or some other privileged account).

If you do not have root access, then refer to the section Unprivileged Installation for information about creating a virtualenv in your user account.

Debian/Ubuntu

Flask-Diamond installs cleanly on Debian and Ubuntu systems released after 2011.

apt-get install python python-dev python-pip build-essential
apt-get install sqlite-dev
pip install --upgrade pip
pip install --upgrade virtualenv
pip install virtualenvwrapper

Redhat

Flask-Diamond can be installed on RedHat, but ensure your package manager is installing Python 2.7; as of August 2015, RHEL provides an older version.

yum install python python-devel python-pip
yum install sqlite-devel
pip install --upgrade pip
pip install --upgrade virtualenv
pip install virtualenvwrapper

OSX with Homebrew

Flask-Diamond installs pretty easily on OSX with Homebrew. Make sure you are using the admin user for this process, just like a normal Homebrew operation.

brew install python --universal --framework
brew install pyenv-virtualenv
brew install pyenv-virtualenvwrapper
brew install sqlite
pip install --upgrade pip

Unprivileged Installation

Sometimes, you do not have root access to the system. It is still possible to use Flask-Diamond, but the installation process is slightly different because it does not use virtualenvwrapper. Instead, you will create your virtualenv directly and use the activate macro to work on it.

curl -O https://raw.github.com/pypa/virtualenv/master/virtualenv.py
python virtualenv.py my-diamond-app
. my-diamond-app/bin/activate
pip install Flask-Diamond

Basics

The basics: how to start a new project and the first steps for making a Flask-Diamond application.

Project Initialization and Scaffolding

The quickest way to start a new Flask-Diamond project is to use the scaffolding system. Scaffolding applies a series of file templates that result in a starter application that can be further customized. This document discusses the configuration questions that are used during scaffolding.

As described in the Quick Start, the basic process looks like this:

  1. Install Flask-Diamond in a virtualenv
mkvirtualenv Flask-Diamond
pip install Flask-Diamond
  1. Scaffold an application in a new directory
workon Flask-Diamond
diamond-scaffold.sh ~/Documents/new-project
cd ~/Documents/new-project
mkvirtualenv -a . new-project
make install docs test db server

About Scaffolding

When you invoke diamond-scaffold.sh, a template is automatically applied. Using mr.bob, a brief set of questions are used to populate the templates with variables. When you answer these questions, your choices are stored in a file called .mrbob.ini that is located in the root folder of your project.

Template questions

This template is suitable for supporting many types of Python projects. It will create a basic directory structure that provide requirements management, documentation, build, deployment, and other basic project needs. This step of the scaffolding process asks the following questions:

1. What is the name of the application (i.e. username, daemon name, etc)?

Example: Flask-Diamond

The application name is a human-readable name that will be used in documentation, on websites, and when talking about the project. This should follow conventions that are established within your community. In the case of Flask-Diamond, the project name tries to follow the naming pattern established by other Flask extensions like Flask-Admin, Flask-Security, and Flask-RESTful.

2. What is the name of the module (can be different from the application name)?

Example: flask_diamond

The module name is a PEP8 styled name. In the case of Flask-Diamond, the module name should be lowercase and hyphens become underscores. Thus, Flask-Diamond becomes flask_diamond.

3. What is the short description for this project?

Example: Flask-Diamond is a batteries-included Flask framework.

This question corresponds to the setuptools.setup(description="") variable in the project setup.py file. The short description is intended to appear as a brief summary in PyPI.

4. What is the long description for this project?

Example: Flask-Diamond is a batteries-included Flask framework. Easily scaffold a working application with sensible defaults, then override the defaults to customize it for your goals.

This question corresponds to the setuptools.setup(long_description="") variable in the project setup.py file. In practice, this usually ends up being multiple paragraphs.

5. Who is the author of this software?

Example: Ian Dennis Miller

This question corresponds to the setuptools.setup(author="") variable in the project setup.py file. It’s a name. Your name.

6. What is the author’s contact email?

Example: iandennismiller@gmail.com

This question corresponds to the setuptools.setup(author_email="") variable in the project setup.py file. This is your public contact information.

7. What is the author’s contact URL?

Example: http://flask-diamond.org

This question corresponds to the setuptools.setup(url="") variable in the project setup.py file. If you have a website that provides support for your project, put it here. In the case of Flask-Diamond, there are lots of resources on the official website, including a link to the project issue tracker. As a result, Flask-Diamond uses the project URL as the contact URL.

8. What ssh key is used to deploy?

Example: ~/.ssh/id_rsa

This question corresponds to fabric.api.env.key_filename inside fabfile.py, which is the SSH key filename that is used to deploy the project to a remote host via SSH.

9. Where is the git repository?

Example: https://github.com/iandennismiller/flask-diamond

If you will be distributing your application through hosted version control, then provide the URL here. This is used during deployment, and it is useful for reference.

10. Which port will the daemon listen on?

Example: 5000

Your application will run as an HTTP service that listens on the port provided here. Thus, if you answer 8000 you will be able to connect to your application at http://localhost:8000/admin.

Automatically generated scaffolding fields

There are some steps in the scaffolding process that are automatically generated for you because they involve random processes.

1. What is the secret key?

Example: \x83.RH\xdc@\x0fu\xb5o\xcd\xf5\xc4\xd5\xb12\xc2M\xca\x96\xc8\xbf\xeb\xde

Flask uses a secret key to seed certain cryptographic functions. To generate a suitable random string for the secret key, use the following Python code:

python -c 'import os; print(repr(os.urandom(24)))'
2. What is the hash_salt?

Example: t52ybrp0oOGHkQEZ

Flask uses a hash salt for password storage. To generate a suitable random string for the hash salt, use the following Python code:

python -c 'import string as s, random as r; \
    print repr("".join(r.choice(s.letters+s.digits) for _ in range(16)))'

Writing an Application with Flask-Diamond

By default, Flask-Diamond is ready to run when you initially scaffold a new application. This document describes the customization process, which transforms the default application into something tailored to your goals. We will start by discussing the default start-up routine for all Flask-Diamond applications, and then talk about modifying start-up to change its behavior.

A basic Flask-Diamond Example

The following example can be generated from a freshly scaffolded project by following the Project Initialization and Scaffolding document with the project name MyDiamondApp. Notice that the MyDiamondApp class inherits from Diamond, which gives the new project a lot of functionality “out of the box.”

from flask.ext.diamond import Diamond, db
from flask.ext.diamond.ext.administration import AdminView, AdminModelView
app_instance = None
from . import models

class MyDiamondApp(Diamond):
    def init_blueprints(self):
        self.super("blueprints")
        from .views.administration.modelviews import adminbaseview
        self.app.register_blueprint(adminbaseview)

def create_app():
    global app_instance
    if not app_instance:
        app_instance = test_app()
        app_instance.init_app()
    return app_instance.app
Customization with Inheritance

In the basic example above, notice the function blueprints(). This function handles the initialization of Flask blueprints. By overloading [1] this functions (and others) within your application, it is possible to customize the start-up behavior of any subsystem. Every subsystem in the Flask-Diamond start-up can be configured. For a complete list of the functions you can overload, refer to the extensions argument in the Diamond object documentation.

Disabling Functionality

Flask-Diamond functionality is initialized using extensions. In case you wish to disable certain functionality, you may simply omit that initialization step from the extensions list. For example, to disable email functionality, omit email initialization from the extensions passed to create_app():

app_instance.init_app(
    extensions=[
        "configuration",
        "logs",
        "database",
        "accounts",
        "blueprints",
        "signals",
        "forms",
        "error_handlers",
        "request_handlers",
        "administration",
        "rest",
        "webassets",
        # "email",
        "debugger",
        "task_queue",
    ]
)

Notice that email has been commented out, but the other extensions are explicitly stated. This ensures the extensions will initialize in the order specified, but that the email extensions will be skipped. Many extensions can be enabled/disabled with ease, including rest, webassets, email, debugger, and task_queue.

Other extensions, like blueprints, are fundamental to the behavior of a Flask application, and are therefore trickier to disable in Flask-Diamond. In those cases, it may be better to override the initialization function using inheritance.

Inheritance also permits functionality to be disabled. For example, to completely disable email, it is possible to overload the email startup with a function that does nothing. It looks like this:

def init_email(self):
    pass # do nothing

Application start-up

Flask-Diamond initializes many subsystems when the application is first started. The subsystems are initialized in this order:

  1. flask_diamond.ext.configuration. the $SETTINGS environment variable is inspected and the file it points to is loaded.
  2. flask_diamond.ext.logs. based on the configuration, write log messages to a file on the filesystem.
  3. flask_diamond.ext.database. connect to a database and initialize the SQLAlchemy Object Relational Mapper (ORM)
  4. flask_diamond.ext.accounts. manage users, roles, login, passwords, and other security things with Flask-Security.
  5. flask_diamond.ext.blueprints. initialize your application’s views (in the MVC sense), which are saved as “blueprints” in a Flask application.
  6. flask_diamond.ext.signals. Flask provides a signals subsystem that your application can hook into to automate certain behaviors.
  7. flask_diamond.ext.forms. initialize your application’s form helpers, which may be global to the forms used in your application.
  8. flask_diamond.ext.handlers. when something goes wrong, you may want to handle it (e.g. by displaying a 404 page)
  9. flask_diamond.ext.handlers. This is the place to create redirections or other custom request handlers that extend beyond views.
  10. flask_diamond.ext.administration. a quick GUI using Flask-Admin with extensive Model support.
  11. flask_diamond.ext.rest. provide a REST API using Flask-RESTful
  12. flask_diamond.ext.webassets. it is possible to bundle assets like images, CSS, and javascript with your application. webassets simplifies some of this work.
  13. flask_diamond.ext.email. send email with Flask-Mail
  14. flask_diamond.ext.debugger. when the configuration specifies that DEBUG = True, the web interface will display a widget with extra debugging tools.
  15. flask_diamond.ext.task_queue. provide a task queue using Celery

Extending the Scaffold

The scaffold files are a starting point, and you will probably end up creating many new files in the course of writing your application. You can think about the scaffold as being sortof similar to inheritance; if you want to change one of the default files, just overwrite it with your own. By customizing the scaffold, you can easily create new models, views, security views, administration views, API endpoints, and more.

Additional scaffolds are distributed along with Flask-Diamond. They are stored in $VIRTUAL_ENV/share/skels and can be applied manually using mr.bob. Additional scaffolds describe common patterns for using Views and Models.

It is recommended to stick with the directory structure in the beginning. As with anything, you are free to change the structure, but if you learn how to work within it, your applications will be easier to maintain and deploy - especially when you have dozens of Flask-Diamond applications to manage!

Further Reading

Several guides have been created to discuss Flask-Diamond application building in greater detail:

Footnotes

[1]“Overloading” is the process of creating a function with the same name as a function in the class you’re inheriting from. In the example above, we have overloaded administration() and blueprints().

Diagram of a Flask-Diamond Scaffold

When a new Flask-Diamond project is scaffolded, it will generate files in roughly the following layout. The SVG scaffold diagram is also available for download.

_images/flask-diamond_project.png

Developer Guide

Once you have a project to work with, more advanced topics are discussed in the Developer Guide. These topics are for developers who are writing or maintaining Flask-Diamond applications.

Model-View-Controller with Flask-Diamond

Model-View-Controller (MVC) is a popular architecture for designing applications that have a user interface. At its heart, MVC is a collection of software design patterns that provide a vocabulary for designing your application. When you “speak MVC,” other people who also know MVC will understand what you are saying.

The MVC vocabulary consists of:

  • Models: a way for talking about data
  • Views: a way for talking about user interfaces
  • Controllers: a way for talking about program logic

This document presents an overview of Model-View-Controller and links to more detailed documentation that discusses these ideas in greater detail.

Model

A model is usually named after a noun. A model is a data representation of something that exists, and just about anything that exists can be modeled.

Entities and Relationships

To model a chess game, you’d start with a model of Players and Chess Pieces, which are entities. A player has many chess pieces, so there is a relationship between our entities. Using nothing more than the idea of “Players” and “Chess Pieces”, you can go a long way towards modeling the game of chess.

All models have two properties:

  • Entities: An Entity is a type of object. Entities have attributes, which are characteristics of the Entity. In the chess example, a Player is a an Entity; there are two Players in chess and each one is an instance of the Player class. An attribute of Players is “color”; the player controls either white pieces or black pieces, so the player’s color can be white or black. Since a player can have a name, name is therefore also an attribute of a Player.
  • Relationships: Entities can affect one another through relationships. In the chess example, a Player has many Pieces and each Piece is owned by a Player. Since a Player can have many Pieces, we call this a “one-to-many” Relationship. There are also one-to-one and many-to-many relationships.

A model can therefore be described using an Entity-Relationship Diagram, which shows all of the types of objects, their attributes, and the way entities relate to one another. Read more about Models with SQLAlchemy for a more detailed discussion and code examples.

A Philosophy of Models

A model might be a very simple representation of a real thing, or the model might be very detailed. A model of an entire country’s economy might require lots of detail, whereas a model of a school district might be relatively simpler.

A model is in some ways a platonic ideal of the actual domain being modeled. While things in the “real world” are irregular in an uncountable number of ways, our models are perfectly regular. Since models are stored in a database, all of the model attributes can be lined up nicely into rows and columns. Tidy!

Paradoxically, a model is always an imperfect representation of the thing it is modeling. The irregularities of the real world are difficult to capture using a model. The goal for good model creation is to isolate the parts of the model that are regular so as to reduce the number of exceptions to your model.

Sometimes, we talk about “domains” when we talk about models, because our models might be thematically related to one another. A domain might be something like finance, gaming, email, or any other broad category that people build applications for. To properly model a domain, we might talk to a “domain expert” to learn more about the kinds of models we are building.

View

A view is a user interface that can present data that comes from a model. In classic MVC, the model pushes data to the view, and the view knows how to update itself to display the data that was received from the model. Views can also contain input elements like buttons, fields, and sliders. When these input elements are activated, the Controller must decide how to respond. Views are often written as templates that have placeholders for data. For web programming, a View template is frequently written using HTML [1]. Read more about Views with Jinja and blueprints for a more detailed discussion and code examples.

Controller

A controller responds to input by changing a view or model. A common type of controller is driven with a Graphical User Interface, which uses things like menus, fields, and buttons so that a human can click stuff to get things done. Read more about GUI with Flask-Admin for a more detailed discussion.

A different type of controller is an API, which is typically used by other software (rather than a human) to make the application do something. Read more about API with Flask-RESTful for a more detailed discussion.

Footnotes

[1]There’s nothing inherently special about HTML templates for constructing Views. For example, Android Views can be constructed using Java. The point is that the idea of views can be generalized to any platform.

Models with SQLAlchemy

In Flask-Diamond, a Model is a way of reading and writing a database. If our application is a chess game, then we’re modeling chess objects in a database. If our application is a social network, then we’re modeling people objects in a database. A fundamental assumption of the Model-View-Controller architecture is that our application deals with objects, and our objects are modeled after the things our application deals with.

This document will demonstrate a model, then discuss some of the ways Flask-Diamond makes it easier to work with models.

A Basic Model

A model is actually written in Flask-Diamond using Python. Models are represented using SQLAlchemy, which is a very powerful Python library for working with databases. Since we are storing our models in a database, SQLAlchemy provides a strong foundation for getting the job done.

Let’s create a model of a person who has a name and an age. Also, let’s model the fact that every person has two biological parents (here called a mother and a father). The following example demonstrates one way this model might be accomplished. [1]

from flask.ext.diamond.utils.mixins import CRUDMixin
from .. import db


class Person(db.Model, CRUDMixin):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    age = db.Column(db.Integer)

    mother_id = db.Column(db.Integer, db.ForeignKey('person.id'))
    mother = db.relationship('Person',
        primaryjoin=('Person.mother_id == Person.id'),
        remote_side="Person.id")

    father_id = db.Column(db.Integer, db.ForeignKey('person.id'))
    father = db.relationship('Person',
        primaryjoin=('Person.father_id == Person.id'),
        remote_side="Person.id")

    def __str__(self):
        return self.name

Now, we can model a Person and specify their name and age. Once we create Person objects for a mother and a father, we can model the parent-child relationships. The model above could be used in Python like so:

from models import Person

me = Person.create(name="Me", age=34)
mom = Person.create(name="Mom", age=65)
dad = Person.create(name="Dad", age=64)
me.mother = mom
me.father = dad
me.save()

Model Methods

Usually, data models are created for entities that are changing. A person’s age increases every year, so we might create a birthday method that increments a person’s age. A simplified version of our person model has been extended with the birthday method below:

class Person(db.Model, CRUDMixin):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    age = db.Column(db.Integer)

    def birthday(self):
        self.age += 1
        self.save()
Only The Model Controls the Data

It is important to contain any data-altering methods within your Model, or else there may be confusion down the road. For example, if there is code within the Controller that directly alters the data Model, you should move that code from the Controller to a new Model method that achieves the same task. Finally, in the controller, invoke the Model method.

SQLAlchemy

SQLAlchemy and Flask-SQLAlchemy are used to provide an Object Relation Mapper (ORM), which reads/writes a database and makes the data easy to access using Python. By using an ORM such as SQLAlchemy, it is possible to avoid many pitfalls of directly using SQL, such as SQL injections or schema mismatches. One of the best resources to learn about writing models is the SQLAlchemy documentation itself, which is both excellent and extensive.

Model Relationships with SQLAlchemy

The SQLAlchemy Basic Relationships document provides an excellent overview of different relationship patterns, including:

To demonstrate a basic relationship, let’s say each Person lives in a House, which is modeled as:

class House(db.Model, CRUDMixin):
    id = db.Column(db.Integer, primary_key=True)
    address = db.Column(db.String(255))

class Person(db.Model, CRUDMixin):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64))
    house_id = db.Column(db.Integer, db.ForeignKey("house.id"))
    house = db.relationship('House',
        backref=db.backref('persons', lazy='dynamic')
    )

The following code example uses the classes above to create two people who live at one house.

our_house = House(address="1600 Pennsylvania Ave")
myself = Person("Me", house=our_house)
mom = Person("Mom", house=our_house)
print(myself.house)
Querying with SQLAlchemy

Based on the Person class, a simple query that finds a person named “Me” looks like:

myself = models.Person.find(name="Me")
print(myself.name)

However, the SQLAlchemy Query API is extremely powerful, and its documentation is the authoritative source.

When the Model Changes

There is a close correspondence between the Model and the database tables. If an attribute is added to a model, then we need a new column in our database to store the values for this attribute. If the model changes, the database must also change. There are two ways of updating your database:

  • the clean slate: delete the old database and creating a new one that reflects the latest changes to the model. This is accomplished with make db on the command line. It’s easy and quick.
  • schema migrations: analyze your updated model to determine what parts are different from your old database, and then add/remove those parts to a live database. This is tricky, but it is necessary for databases in production. Read more in Database Schema Migration with Flask-Migrate.

As long as you are actively developing, it is recommended to use make db each time you update your model. However, when your application is live, you will need to read Database Schema Migration with Flask-Migrate to learn about altering a production database.

Data Fixtures

What good is a data model without any data to put in it? Data fixtures are a way of easily adding data to your database, which is helpful when you are frequently rebuilding your database with make db. Data fixtures can be placed into bin/manage.py within the populate_db() function. If you find yourself continually re-creating certain model objects in your database so you can test your application, then consider using populate_db() to automate the creation of these objects.

For example, in order for make db to automatically create a Person object based on the Person class above, construct populate_db() like this:

@manager.command
def populate_db():
    "insert a default set of objects"

    from models import Person

    me = Person.create(name="Me", age=34)
    mom = Person.create(name="Mom", age=65)
    dad = Person.create(name="Dad", age=64)
    me.mother = mom
    me.father = dad
    me.save()

Another model example

class Individual(db.Model, CRUDMixin):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))

    friend_id = db.Column(db.Integer, db.ForeignKey('individual.id'))
    friend = db.relationship('Individual',
        primaryjoin=('Individual.friend_id == Individual.id'),
        remote_side="Individual.id")

    def set_friend(self, obj):
        self.friend = obj
        self.save()

    def as_hash(self):
        pass

    def __str__(self):
        return self.name

Further Reading

Footnotes

[1]Note the use of CRUDMixin, which provides us with a create() method. For more information about CRUDMixin, see CRUD with Flask-Diamond.

Views with Jinja and blueprints

As is explained in Model-View-Controller with Flask-Diamond, a View takes data from a Model and presents it (typically to a user). Often times, there are multiple Views of a data Model, and they may present different aspects of the Model. A common pattern for multiple views is the use of permissions to restrict functionality to users based upon their account role. The administrator may have a special View into the data that provides extra functionality that regular users do not have.

Views are frequently written using templates, which have placeholders for variables that may be filled in by the application. In Flask-Diamond, the Jinja templating language is used to generate HTML, javascript, CSS, and other web-facing files that create a user interface. By populating the template with data from the Model, the result is that users can interact with Model data.

Jinja is a Templating Language

The rest of this document provides a summary of the key points about Jinja Views. The Jinja website provides a nice example of what Jinja looks like:

{% extends "layout.html" %}
{% block body %}
  <ul>
  {% for user in users %}
    <li><a href="{{ user.url }}">{{ user.username }}</a></li>
  {% endfor %}
  </ul>
{% endblock %}

A detailed discussion of templates is available from the Jinja templates documentation.

Variables in Jinja

A python expression within a Jinja template is denoted using double-curlies as {{ }}. In this manner, it is easy to reference variables by simply placing them inside double-curlies. In the example above, user.username will be replaced by the actual value of a user’s username (e.g. “administrator”). If it were not inside the double-curlies, “user.username” would be printed verbatim (i.e. substitution is not performed unless inside double-curlies).

Statements in Jinja

A python statement (like an “if statement”) is denoted in Jinja using {% %}. Using statements, it is possible to create dynamic templates that print different details based upon the data given to them. For example, in a chess game, each player would view the chess board from opposite sides of a table, so the view should display the board differently to each player. A logic statement like {% if player.color=='white' %} can be used to display the board in one direction to the white player, and {% if player.color=='black' %} can do the opposite.

Rendering a template with Flask

Flask provides an easy mechanism for working with templates that is called render_template(). When a template is rendered with data, the placeholders inside the template are replaced with the data. In the case of web applications, the result is typically an HTML document that presents the application’s data model in a format that a human could use through a web browser.

To render the previous template, first save it to a file called “my_template.html” and place that in a directory called “templates”.

The code looks like:

my_data = {"users": [{"username": "administrator", "url", "http://utoronto.ca"}]}
return flask.render_template("my_template.html", **my_data)

Flask searches for templates by looking through a directory called “templates”. This behaviour can be extended by using Blueprints, which are explained a little later in this document. More information about Flask and templates is available from the Flask templating documentation.

Routing a View

URLs are used within web applications to identify Views. In the case of a chess game application, the URL /piece_list could return with a summary of all the chess pieces, and /piece/black/pawn/1 could provide information about the black player’s first pawn.

When building an MVC View for a web application, the Controller is responsible for actually routing the user to the View. MVC Web applications use the URL to connect with a view, such that a user can use their web browser to request /user/login_form in order to log in to a website or view the /player/white/pieces to figure out the score in a game of chess. In Flask, the route() decorator is used to apply a route to a View function.

A simple route looks like:

@app.route('/')
def index():
    return "Hello world!"

The Site Map is used to determine all the routes that will be necessary for exposing a web application. Every View must be present within the Site Map, and there must be a unique URL for each View.

It is possible to look at a Flask-Diamond web application site map from within the application itself:

print flask.current_app.url_map

Flask Blueprints: a Collection of Views

Often times, there will be several related views and it makes sense to collect these together using a Flask Blueprint. When this happens, the views are collected into a URL subdirectory, and individual views are nested within that URL. For example, in a game of chess, there may be several views related to players and several related to pieces, which could be collected into the /player directory and the /piece directory.

The Flask documentation demonstrates an extremely simple blueprint:

from flask import Blueprint, render_template, abort
from jinja2 import TemplateNotFound

simple_page = Blueprint('simple_page', __name__,
                        template_folder='templates')

@simple_page.route('/', defaults={'page': 'index'})
@simple_page.route('/<page>')
def show(page):
    try:
        return render_template('pages/%s.html' % page)
    except TemplateNotFound:
        abort(404)

This example demonstrates everything we have discussed so far:

  • the render_template function
  • the template_folder that contains Jinja templates
  • routing the View to a URL with route()

The Flask documentation also explains how to register a blueprint with an application:

from flask import Flask
from yourapplication.simple_page import simple_page

app = Flask(__name__)
app.register_blueprint(simple_page)

Views within Flask-Admin BaseModelView

In Flask-Admin, each BaseModelView is actually a Blueprint that provides views for creating, reading, updating, and deleting model objects. The BaseModelView template behaves much like a regular blueprint, except:

In this manner, it becomes easy to extend a CRUD with custom methods that go beyond create, read, update, and delete.

Another admin view

{% extends 'admin/model/edit.html' %}

{% block body %}
    {% block model_menu_bar %}
    <ul class="nav nav-tabs">
        <li>
            <a href="{{ url_for('.index_view') }}"><i class="icon-list-alt"></i> {{ _gettext('List') }}</a>
        </li>
        <li class="active">
            <a href="{{ url_for('.index_view') }}"><i class="icon-eye-open"></i> Individual</a>
        </li>
    </ul>
    {% endblock %}

    {% block model_content %}
        <h2>Individual</h2>

        <ul>
            <li>ID: <b>{{ model.id }}</b></li>
        </ul>
    {% endblock %}

{% endblock %}

API with Flask-RESTful

One of the most common ways to build an API is using a paradigm called REST. Flask-RESTful is a great library for Flask that simplifies the creation of a REST API. This document describes REST and how to implement it in a Flask-Diamond project.

REST in a Nutshell

I like to simplify REST like so:

  • The web consists of documents that are referred to by URL links.
  • Each URL is a “thing”. As a part of speech, it is a noun. For example: a URL points to a picture, a document, or a person’s timeline.
  • The language spoken by web browsers and web servers is called HTTP. This is the protocol for “hypertext”.
  • HTTP provides actions (verbs) like GET, PUT, DELETE, and POST.
  • Using HTTP and URLs provides verbs and nouns that we can use to write statements, like “GET a Picture” or “DELETE a Document”.

REST is a strong statement about the grammar of the web. It tells us where the nouns are and where the verbs are. If we learn to make APIs that are RESTful, then we can count on others to be able to use our APIs in natural ways to create complex expressions.

Examples of REST

Let’s say you are building an API for chess. The main thing players must do is change the lay of the board by moving pieces. Our API therefore exposes one endpoint: /api/board. To change the board, we will update it by providing a standard chess move that takes a piece from one location and moves it to another. We will use JSON to represent the move like so:

{
    'from': 'A2',
    'to': 'C3'
}

So, to put a knight into a new spot, we might issue the following HTTP command:

PUT /api/board HTTP/1.1

{'from': 'A2', 'to': 'C3'}

Such a command could be issued using javascript, from the command line (e.g. with curl), or using any HTTP client. That’s the beauty of REST: if you work with the web, the web can work with you.

Flask-RESTful

It is easy to create RESTful APIs with Flask-RESTful, which is already integrated into Flask-Diamond.

The primary way to use Flask-RESTful is to create Resource objects. A Resource is a “thing” that your API will expose. You can easily add API Resources to your Flask-Diamond application by placing them into a function called init_rest():

def init_rest():
    class Board(Resource):
        def get(self, id):
            return models.board.get(id).dumps()

        def put(self, id):
            board = models.board.get(id)
            board.move_piece()

    rest.add_resource(Board, '/api/board/<int:id>')

This simple example creates a resource called Board. Then, it specifies a way to handle the GET verb, which will result in retrieving a board. It also specifies how to handle the PUT verb, which it will respond to by moving a piece. Finally, the resource is exposed using a URL: /api/board/....

If you were to place this code into your own application alongside your init_blueprints() function, you will suddenly have an API for your app!

MarshmallowMixin

flask_diamond.mixins.marshmallow.MarshmallowMixin simplifies object marshalling, which is the process of mapping data to and from a serialization format like JSON. This functionality is provided by Marshmallow and Flask-Marshmallow.

Marshalling is useful because applications must frequently send model data across the Internet, and in order to do so, models are commonly translated into JSON or another format. For example, date and time objects are native Python objects that must be converted to a string in order for JSON to transmit them. Marshalling makes serialization and deserialization into a repeatable process.

Let’s look at the following line of code:

return models.board.get(id).dumps()

The dumps() at the end of the line will cause an ORM model object to be converted to JSON using Marshmallow. For more information, please see the documentation for Marshmallow and Flask-Marshmallow.

How not to REST

REST says we should not create URLs that imply actions; URLs must be things. This pattern is common in older websites. For example, you should not build an API with a URL called /api/move_piece because that describes an action: moving a piece. REST says the only actions available are HTTP verbs, like GET and PUT.

You can think about it this way too: when a web client visits a URL, it usually issues the GET verb. It doesn’t really make sense to GET /api/move_piece. However, early web developers tried to make this work using arguments. That resulted in operations like GET /api/move_piece?from=A2&to=C3. These became really long URLs that contained all the same information as the RESTful example, but which broke certain features of the Internet. For example, if a web spider visited that URL, the web server would incorrectly interpret that visit as a command to move a piece.

This leads to chaos. Don’t make URLs this way. Be RESTful, instead.

GUI with Flask-Admin

A common pattern in application design is to apply CRUD to your Model, and then provide a Graphical User Interface for people to interact with the Model. Flask-Admin makes it very easy to create a basic interface with Create-Read-Update-Delete functionality, and provides a framework for designing much more sophisticated interfaces.

This document discusses a simple CRUD with Flask-Admin, and then extends the CRUD with additional functionality.

A Simple CRUD GUI

When Flask-Admin creates a GUI, it automatically discovers the Model Attributes and creates a web form containing fields for all the attributes. Flask-Admin provides the BaseModelView class as the foundation for building Views that a user can interact with to inspect and control a Model. Flask-Diamond provides AdminModelView, which applies an authentication requirement so that only users with the Admin role can access the View.

AdminModelViewExample

The following example imports a class called Person (which is described in the Model documentation). Then, the Person class is added to the GUI using the add_view() method.

from flask.ext.diamond import Diamond, db, security
from flask.ext.diamond.administration import AdminModelView
from . import models

class my_diamond_app(Diamond):
    def administration(self):
        admin = super(my_diamond_app, self).administration()
        admin.add_view(AdminModelView(
            models.Person,
            db.session,
            name="Person",
            category="Models")
        )

When the server is launched, it provides a GUI facility for applying CRUD operations to the Person model. Due to the use of AdminModelView, the application will now require a password before allowing anybody to create, read, update, or delete any Person object.

The category parameter causes the GUI to put this View into the menu bar beneath the heading “Models”. The name parameter indicates this link will be titled Person. Now you can find the Person CRUD by navigating through the menu to Models and then to Person.

Multiple CRUD Views Inside BaseModelView

BaseModelView actually creates multiple views that provide CRUD functionality:

  • Create: this view presents a form with all of the model attributes displayed as blank fields. When the fields are populated and the form is submitted, a new model object will be created.
  • List: the List view displays a paginated list of all the objects of the Model. This view also contains widgets for editing and deleting objects.
  • Edit: the Edit view is identical to the create view, except now the fields are populated by a specific model object. When the fields are changed, the model object can be updated.
  • Delete: The Delete view simply confirms whether the user wants to delete an object.

Create-List-Edit-Delete corresponds directly to Create-Read-Update-Delete.

Extending the CRUD

Flask-Admin makes it pretty easy to add custom functionality through Python class inheritance [1]. In the Model documentation, the Person Model provides a birthday() method that causes the person to become one year older. The following sections demonstrate how to expose the birthday method and create a user interface widget for calling that method.

Exposing a new View

In addition to the basic CRUD views, new views can be created for doing other things with Models. Since the Person class has been extended with a birthday() method, the following example enables the method to be called through the Controller.

from flask.ext.admin import expose

class PersonModelView(AdminModelView):
    @expose('/birthday/<person_id>')
    def birthday(self, person_id):
        the_person = models.Person.get(person_id)
        the_person.birthday()
        return flask.redirect(flask.url_for('.list_view'))

class my_diamond_app(Diamond):
    def administration(self):
        admin = super(my_diamond_app, self).administration()
        admin.add_view(PersonModelView(
            models.Person,
            db.session,
            name="Person",
            category="Models")
        )
Adding a Widget

One simple way to add functionality to the user interface is to use Flask-Admin’s formatters to make a field into an interactive widget. This basic pattern is demonstrated by formatting Person.age with a “birthday” button:

import jinja2

class PersonModelView(AdminModelView):
    def age_formatter(self, context, model, name):
        age_widget_template = "{0} <a href='{1}'>birthday!</a>"
        age_widget = age_widget_template.format(
            model.age,
            flask.url_for(".birthday", person_id=model.id)
        )
        return jinja2.Markup(age_widget)

    column_formatters = {
        "age": age_formatter,
    }

When these two PersonModelView examples are combined, the result is a user interface that can model a Person’s birthday when a link is clicked.

ModelView Example

The following AuthModelView includes examples for overriding various fields within the model view. The full documentation for ModelView should be consulted for more information, but this example is intended to describe how that information may be applied within a Flask-Diamond project.

class IndividualAdmin(AuthModelView):

    edit_template = 'individual_view.html'

    column_list = ("name", "friend")

    form_overrides = {
        "upload_buffer": FileUploadField
    }

    form_args = {
        'upload_buffer': {
            'label': 'Report PDF',
            'base_path': "/tmp",
        }
    }
More Flask-Admin

Flask-Admin is really powerful, and the best way to learn more is by reading the Flask-Admin documentation.

Further Reading

Footnotes

[1]Incidentally, Python class inheritance is the same mechanism used by Flask-Diamond for customization. Inheritance is discussed further in writing_an_application_with_flask-diamond.

CRUD with Flask-Diamond

Create Read Update Delete

CRUD stands for Create, Read, Update, and Delete. The CRUD pattern complements Model-View-Controller by providing a standard set of methods that can be applied to most types of models. CRUD acts like a simple API for models in Flask-Diamond. The four CRUD actions describe the life-cycle of a model object:

  1. First, an object is created with certain attribute values.
  2. The object may be read to get the value of those object attributes.
  3. The object values may be updated to update the model based on changes in the world.
  4. Finally, the object is deleted to remove it from the database.

CRUDMixin

flask_diamond.mixins.crud.CRUDMixin, which has been adapted from Flask-Kit, will extend your model with functions for create(), read(), update(), and delete(). The default Flask-Diamond scaffold provides an example of CRUDMixin usage:

from flask.ext.diamond.utils.mixins import CRUDMixin
from .. import db

class Person(db.Model, CRUDMixin):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    age = db.Column(db.Integer)

Notice that class Person() inherits both db.Model and CRUDMixin. Now the following CRUD workflow is possible:

myself = Person.create(name="me", age=34) # create
print "{name} is {age} years old".format(myself)
myself.update({'age': 35}) # update
print "{name} is now {age} years old".format(myself)
myself.delete()

Flask-Admin CRUD

Flask-Admin provides Model CRUD functionality with its BaseModelView class. BaseModelView is an extremely powerful tool for rapidly implementing a web-based CRUD Graphical User Interface that makes it easy to create, read, update, and delete model objects using a web browser.

The following application instantiates a CRUD for the Person model described above.

from flask.ext.diamond import Diamond, db
from flask.ext.diamond.administration import AdminModelView
from . import models

class my_diamond_app(Diamond):
    def administration(self):
        admin = super(my_diamond_app, self).administration()
        admin.add_view(AdminModelView(
            models.Person,
            db.session,
            name="Person",
            category="Admin"
            )
        )

Further Reading

Database Schema Migration with Flask-Migrate

Any time the data model is changed, the database must be updated so that it has the right columns. Any time a table or column is added, removed, or changed in any way, we say the database schema [1] has changed. We need to be sure the database schema always matches the model. This common task is called schema migration, and it is handled well by Alembic.

Flask-Diamond makes it quick and easy to rebuild the schema during development with make db, which will delete the development database and rebuild it with all new columns matching your models. However, it’s a little drastic to always throw everything away and start from scratch. Furthermore, when the data in your database is important, it’s actually impossible to throw it away and start over. Luckily, there’s Flask-Migrate, which makes Alembic integration easy so we can handle migrations smoothly.

Introducing Migrations

Schema migrations, which are handled using Flask-Migrate, can be used to change in-production databases by recording the minimal set of steps will make your database schema match your model. Migrations are versioned, so it is possible to experiment with schemas before deploying them. It is also possible to roll back schemas in case something went wrong.

Creating a Migration

A database migration is potentially complex, so creating one takes several steps.

  1. find the location of the dev database
  2. delete the development database
  3. build the database using migrations
  4. create a new migration
  5. rename the file with a short summary
  6. edit the migration
  7. test the migration
  8. done
find the location of the dev database

We will work on the dev database, which should not have any important data in it.

grep SQLALCHEMY_DATABASE_URI etc/dev.conf
delete the development database

We need to start from scratch.

rm /tmp/my-application.db
build the database using migrations

In other words, we build the database without the python Model.

make upgradedb
create a new migration

This works by comparing python model to last migration version

make migratedb

This will create a new python script within the migrations/versions subdirectory of your application module directory. There will be two functions automatically created for you: upgrade() and downgrade(). The upgrade() function will individually add tables and columns to your old database schema until it matches your current model. The downgrade() function is the opposite. In this manner, it is possible to “roll back” to a previous schema.

rename the file with a short summary
cd ${MODULE}/migrations/versions
mv ${CHECKSUM}_.py ${CHECKSUM}_${SUMMARY}.py
edit the migration
  • for sqlite, remove all drop_column() operations
  • for sqlite, remove all create_foreign_key() operations
test the migration
  • delete the database again (rm /tmp/daemon-dev.db)
  • upgrade using migrations (make upgradedb)
  • perform a new migration (make migratedb)
  • verify that it is empty (i.e. all tables are reflected)
  • delete the empty migration file (rm ${MODULE}/migrations/versions/${CHECKSUM}_.py)
done

The migration is ready. It can now be applied in the production environment. First, ensure you are using a configuration file that specifies the target database you will apply the migration to. Then, invoke the schema upgrade directly.

export SETTINGS=/etc/production.conf
manage.py db upgrade

An Example Migration File

It is easy to see how a migration uses SQLAlchemy directly to create tables if we examine an example. One of the migrations that ships with Flask-Diamond is in flask_diamond/migrations/versions/20f04b9598da_flask-diamond-020.py. In this case, the upgrade() function adds several new columns to the user table. The file looks like this:

"""update to flask-diamond 0.2.0

Revision ID: 20f04b9598da
Revises: cf0f5b45967
Create Date: 2015-02-07 22:54:24.608403

"""

# revision identifiers, used by Alembic.
revision = '20f04b9598da'
down_revision = 'cf0f5b45967'

from alembic import op
import sqlalchemy as sa


def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.add_column('user', sa.Column('current_login_at', sa.DateTime(), nullable=True))
    op.add_column('user', sa.Column('current_login_ip', sa.String(length=255), nullable=True))
    op.add_column('user', sa.Column('last_login_at', sa.DateTime(), nullable=True))
    op.add_column('user', sa.Column('last_login_ip', sa.String(length=255), nullable=True))
    op.add_column('user', sa.Column('login_count', sa.Integer(), nullable=True))
    ### end Alembic commands ###


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_column('user', 'login_count')
    op.drop_column('user', 'last_login_ip')
    op.drop_column('user', 'last_login_at')
    op.drop_column('user', 'current_login_ip')
    op.drop_column('user', 'current_login_at')
    ### end Alembic commands ###

Applying a Migration

To apply a migration to the development database, enter the virtualenv and run:

make upgradedb

This will inspect your database and automatically apply migrations, in order, until it is at the latest. By default, this applies the migration to your development database.

Migrations in Production

In order to affect the production database, you must set SETTINGS so that it points to your production configuration. Then, you must invoke Flask-Migrate explicitly, like so:

bin/manage.py db upgrade
Displaying a Migration as SQL

It can be helpful to inspect a migration before it is applied to the database. The following command will display a preview of the changes that will be made once a migration is applied:

bin/manage.py db upgrade --sql

Accessing Flask-Migrate directly

In fact, the full functionality of Flask-Migrate is easily available on the command line:

bin/manage.py db help

Version Control and Migrations

Because each migration has a unique checksum, and because each migration is in a separate file, it is easy to use a version control mechanism like git to closely control your schemas.

Footnotes

[1]A database schema is a list of all the tables in a database, all the columns in those tables, and the data types for each column. Schemas are often expressed using SQL CREATE statements, which is a concise way of describing exactly which tables and columns need to exist.

Documentation with Sphinx

As projects grow in size, the need to create documentation increases. The Sphinx Project has put forth a very robust Python documentation solution. As a result, Flask-Diamond provides a starter template for creating your own documentation using Sphinx.

Where are the files

You will find a starter set of documentation in the /docs folder of your project. The first file you need to edit is called /docs/index.rst, and from there build the table of contents to describe your project. Images and media may be placed in /docs/_static. These source files will be transformed into a documentation website that can be browsed online.

Sphinx

Sphinx is a structured document generator application. As a project author, you create documents that describe your project, and Sphinx will take care of the Table of Contents, links between documents, and all of the other parts that make it easy for somebody to navigate the documentation. Sphinx is particularly well suited to creating documentation websites, in which case Sphinx will automatically apply a theme to every page. The end result of using Sphinx is that your project will have an extremely functional online documentation website.

Restructured Text

Documentation in Sphinx is written using a markup language called ReStructured Text (quickref). While this language has some similarities to Markdown, it has many extensions that are suited to document structuring. As a result, Restructured Text makes it easy enough to manage links between pages, make references to specific Python classes, and add footnotes.

In my experience, the learning curve on Restructured Text is steeper than I expected. As a result, you need to persevere, but the payoff is totally worth it. The best place to start is with the ReStructured Text documentation from the Sphinx website.

autodoc

Sphinx can use the autodoc extension to look at your project’s file structure and determine all of the classes in your project. This can be extremely useful for generating API documentation. Sphinx can also extract method and object signatures, which makes it easy to describe all of the parameters for everything.

docstrings

Sphinx autodoc is also able to scan your source code for comments that have been formatted for Sphinx using Restructured Text. Here is an example taken from the flask_diamond.ext.accounts.init_accounts() Flask-Diamond source code:

def init_accounts(self, app_models=None):
    """
    Initialize Security for application.

    :param kwargs: parameters that will be passed through to Flask-Security
    :type kwargs: dict
    :returns: None

    >>> def ext_security(self):
    >>>    super(MyApp, self).ext_security(confirm_register_form=CaptchaRegisterForm)
    """

The comment above will be used to create documentation by Sphinx. Special directives like :param: are used to specify details about the documentation that autodoc cannot determine on its own.

Building the documentation

In the project on the command line:

make docs
Output

The results of make docs will end up in var/sphinx/build. You can open that folder in your web browser to view the documentation.

ReadTheDocs

It is easy to integrate Flask-Diamond applications with readthedocs.org due to the use of Sphinx. A special file called .readthedocs.txt is included with the project scaffold. This file contains a reduced set of Python requirements that may make it easier for RTD to build your project.

Email with Flask-Mail

Lots of applications need to send emails in the course of their operation. Email remains one of the best sources of identity on the Internet today. Flask-Mail is an easy way to integrate email-sending capabilities into your application.

Configuration

By default, flask_diamond.ext.email is already enabled in flask_diamond.Diamond.init_app(), so there is nothing special to do. The project created by diamond-scaffold.sh has email commented out by default, so you will have to enable this in your project’s __init__.py file.

The settings for the SMTP server are located in your configuration file. The relevant lines are these:

MAIL_SERVER = '127.0.0.1'
MAIL_PORT = 25
MAIL_USE_TLS = False
MAIL_USERNAME = None
MAIL_PASSWORD = None

You will need to set this configuration based on your particular email setup.

SMTP Server

In order for your application to be able to send emails, you will need access to an SMTP server. It is possible to use an email provider such as Gmail, but be aware that you won’t be able to route a very high volume of email through a service like that. Often times, you can use a web hosting provider for higher volumes. Since every situation is different, you will need to track down your mail server configuration on your own.

Testing with Nose

Writing tests is an important part of building an application. Especially with dynamically typed languages such as Python, testing is one of the only ways to determine the correctness of your implementation. Python has built the concept of Unit Tests into the core library. Unit Testing is a technique for validating units of your program by making assertions about the behaviour of that code. If any assertions turn out to be false, then you know your code isn’t correct.

Nose

In the interest of making test as automatic as possible, Flask-Diamond uses Nose for test discovery, which will find and run all your tests without having to create a test harness. As an application becomes more developed, it may become necessary to build a test harness for the application. However, at the start of application development, it is usually easier to use test discovery in order to iterate faster.

Nose will automatically search through the /tests folder for any files starting with the name “test” that have functions in them that also start with “test”.

Running Tests

It is possible to invoke the testing subsystem from the command line. The following different testing methods are available in Flask-Diamond:

Run every test

Find every test and run it.

make test
Run individual tests

Run tests identified with decorator @attr("single"). These “single” tests can be used to make testing much faster by skipping all of the other tests.

make single

The following code snippet contains a simple test with the single attribute decorator applied to it.

from nose.plugins.attrib import attr
from flask.ext.testing import TestCase
from flask.ext.diamond.mixins.testing import DiamondTestCaseMixin

class BasicTestCase(DiamondTestCaseMixin, TestCase):
    @attr("single")
    def test_basic(self):
        assert True
Automatically run individual tests

This functionality will watch the project folder for files to change. When a file has changed, it will re-run tests identified with @attr("single"). This feature is designed to make it very quick to get feedback on the performance of your code.

make watch

xUnit

It is possible to automatically interpret the results of testing using the xUnit framework. Nose xUnit output can permit tools like Jenkins CI, Travis CI, or Atlassian Bamboo to capture the results of testing.

Users Accounts with Flask-Security

User accounts are a common requirement for many applications. Another common requirement is Roles, which can be used to grant certain users access to specific functions. Both Users and Roles are provided in Flask-Diamond by Flask-Security, which provides a nice interface for controlling these models.

User and Role Models

Flask-Diamond is already set up with flask_diamond.models.user and flask_diamond.models.role, which provide everything needed for a simple setup. You will notice in your application that models/__init__.py then imports these classes from Flask-Diamond. In this manner, your application will incorporate these models into its own schema.

Overloading the User Model

With an application of moderate complexity, it may be necessary to store additional user data, and certain data may just be simple to put in the User model directly. The following code snippet describes an init_security() function that can be used to implement your own User model.

def init_security(self):
    self.super(app_models=models)

With that in place, ensure models__init__.py imports your own User model instead of importing from Flask-Diamond.

Flask-Admin Integration

You can use user accounts in lots of ways, but one common pattern for Users is to create a password-protected user interface. To make this easier, the Flask-Diamond scaffold includes templates in views/administration/templates/security that permit you to customize the look of login, password maintenance, account registration, and password reset. The templates already inherit from Flask-Admin, but by editing the templates in your project, you can customize them to other scenarios too.

Debugging a Flask-Diamond Application

To simplify debugging, Flask-Diamond provides a basic setup for logging so that your application can write trace data to a file for your inspection. Flask-Diamond also integrates Flask-DebugToolbar to provide a powerful debugger shell directly in the interface.

Logs

Flask-Diamond creates a logging object during initialization. You can write log messages to this object with the following basic code:

flask.current_app.logger.debug("this is a debug-level message")

flask.current_app.logger.warn("important! this is a warning message")

The destination for log files is controlled in the configuration file. During development, dev.conf will write log messages to the var/logs path of your project.

Log Level

During debugging, it is sometimes useful to get extra information about your application while it is running. The log level is used to control how critical logging is. When the level is DEBUG, then all messages are printed - this can be verbose. When the level is INFO, then debugging messages are not printed at all, but general information messages are. When the level is WARN, then only important messages are printed. This level may be suitable for production.

DebugToolbar

Flask-DebugToolbar is a useful web gadget that can help developers to diagnose problems within the browser. In practice, you may end up doing most of your development in a terminal, but the Debug Toolbar can nevertheless be handy for casual projects.

In the application configuration file, whenever DEBUG=True the DebugToolbar will be active. By default, the development configuration enables debugtoolbar whereas the production configuration disables it. It is possible to completely remove the DebugToolbar by ensuring the debugger extension is not loaded at all during flask_diamond.Diamond.init_app().

User Guide

The User Guide topics are for managing and installing a Flask-Diamond application. In particular, IT Ops and Deployment Engineers will benefit from this section.

Requirements Management with virtualenv

The preferred way to manage your Python requirements is with virtualenv. The preferred way to manage virtualenvs is with virtualenvwrapper, which provides a regularized interface for creating and using virtualenvs.

When you install your Flask-Diamond application inside a virtualenv, you are able to “freeze” the state of your installed Python libraries. This way, your application will never suffer from broken requirements due to a system-wide upgrade. This document describes the typical workflow for using virtualenv to manage a Flask-Diamond project.

Pre-requisites

You need to install the minimum System Requirements in order to have the necessary tools for this process. It is assumed that you now have workon and mkvirtualenv available. If you cannot call these commands in a terminal, then you should double-check your System Requirements.

Making a New virtualenv

The way to initialize a new virtualenv is with mkvirtualenv, which is described on the mkvirtualenv documentation. The following example demonstrates creating a virtualenv called my-diamond-app.

$ mkvirtualenv my-diamond-app
New python executable in my-diamond-app/bin/python2.7
Also creating executable in my-diamond-app/bin/python
Installing setuptools, pip...done.
(my-diamond-app) $

Upon creating the virtualenv, it will be activated and your shell prompt changes to include your project name as a prefix. You can verify that you are inside the virtualenv by echoing an environment variable to the screen:

(my-diamond-app) $ echo $VIRTUAL_ENV
~/.virtualenvs/my-diamond-app

Using it

When you need to work on your project, you must activate your project’s virtualenv using workon. After calling workon, your shell’s search path will be updated so that scripts from your virtualenv will be called before any globally installed scripts.

$ workon my-diamond-app
(my-diamond-app) $

In order to leave the virtualenv and return to a normal shell, use deactivate.

(my-diamond-app) $ deactivate
$

pip and Installation

When you use pip inside your virtualenv, it will automatically install packages locally, instead of installing them at the system level. This way, it’s easy to install anything without needing root access. In the following example, we are inside our virtualenv and we want to upgrade pip:

(my-diamond-app) $ pip install --force -U pip
Collecting pip
  Downloading pip-7.1.0-py2.py3-none-any.whl (1.1MB)
    100% |████████████████████████████████| 1.1MB 172kB/s
Installing collected packages: pip
Successfully installed pip-7.1.0
(my-diamond-app) $

Freezing Python Requirements

pip provides a handy mechanism for printing all of the installed libraries, along with their versions. When called within your virtualenv, it produces a snapshot of all your project requirements so you can easily re-install them later.

(my-diamond-app) $ pip freeze
Jinja2==2.7.3
MarkupSafe==0.23
mr.bob==0.1.1
requests==2.7.0
virtualenv==1.11.6
virtualenvwrapper==4.2
(my-diamond-app) $

You can also store your requirements in a requirements file.

(my-diamond-app) $ pip freeze > requirements.txt

Makefile support

By default, Flask-Diamond provides make install, which will use requirements.txt to install your project’s pre-requisites automatically.

Configuration Explanation

The use of configuration files permits your application to easily adapt to multiple environments. Often times, different systems will use different path structures, user accounts, database configurations, and TCP ports. Flask-Diamond implements the practices suggested by Flask 0.10, and by default stores these config files in the ./etc folder of your project.

The SETTINGS Environment Variable

Flask-Diamond will load its configuration from whatever file is referenced by the $SETTINGS environment variable. You can use $SETTINGS to easily manage several profiles for your application. The following example demonstrates choosing the development profile stored in dev.conf.

export SETTINGS=$PWD/etc/dev.conf

Another common way to control $SETTINGS is to use it as a prefix in front of a command. In the following example, the script bin/manage.py is invoked with the dev.conf profile to start the embedded HTTP server:

SETTINGS=$PWD/etc/dev.conf bin/manage.py server

To start the server with the production.conf environment:

SETTINGS=$PWD/etc/production.conf bin/manage.py server

Examples of Configurations

Flask-Diamond ships with a few configuration profiles to get you started:

dev.conf

The development environment is typically used on your developer workstation. You probably have root access to the machine. Any databases are probably temporary in nature, and exist mostly for testing purposes.

production.conf

The production environment is typically your front-facing web server (a “live server”). In this case, the application is probably not running as root. In fact, you may not even have root access to this machine. Thus, you must choose filenames for logging output that are owned by the application user’s account. The production database is also likely to have different permissions, and unlike the development database, the production database probably has important information on it that you want to protect.

testing.conf

For testing purposes, there is a special configuration that writes to a temporary database that is created and destroyed during tests.

Makefile Support

If you inspect the Makefile, you will see that $SETTINGS=$PWD/etc/dev.conf appears before most commands. Most cases will use dev.conf by default in order to protect against accidentally performing tasks upon the production database. Those prefixes are hardcoded so that a command like make db (which resets the database from scratch) cannot easily be applied to the production database.

Flask-Diamond Configuration Variables

By default, Flask-Diamond expects the following variables to be present within a configuration file.

Project

The project is configured with the following directives.

PROJECT_NAME = "Flask-Diamond"
PORT = 5028
LOG = "var/log/dev.log"
LOG_LEVEL = "DEBUG"
SQLALCHEMY_DATABASE_URI = "sqlite:////tmp/flask_diamond-dev.db"
SECRET_KEY = "av^\x81\x03\xd7\xd1\xbd\x92~b\x00\xe8\xf7n9\x0e\xf8i\xdb\xba'\xa9\xea"
BASE_URL = "http://flask-diamond.org/my-diamond-app"
  • PROJECT_NAME: the human-readable name of the project
  • PORT: which TCP port will the HTTP server listen on?
  • LOG: the filename to log messages to
  • LOG_LEVEL: control the verbosity of logging output; DEBUG prints everything, INFO prints less, and WARN will only display problems.
  • SQLALCHEMY_DATABASE_URI: a URI that points to your database. See Flask-SQLAlchemy for more examples.
  • SECRET_KEY: a randomly-generated string that you created during scaffolding
  • BASE_URL: the canonical name for your web application
Debugging

Debugging instructs the application to print extra information during operation. For example, there may be more verbose logging and it may be possible to inspect the application internals. All of this is helpful during development, but can be extremely dangerous in production.

DEBUG = False
DEBUG_TOOLBAR = True
DEBUG_TB_INTERCEPT_REDIRECTS = False
  • DEBUG: when debugging is enabled, Flask produces tracebacks when an exception is encountered.
  • DEBUG_TOOLBAR: whether to include Flask-DebugToolbar, which is a helpful in-browser debugging widget.
  • DEBUG_TB_INTERCEPT_REDIRECTS: it is possible to inject the debug toolbar before URL redirects, which can be helpful for isolating routing problems.
Accounts and Security

Flask-Security provides an integrated platform of account security features, and Flask-Diamond incorporates most of its functionality. The following directives control Flask-Security.

SECURITY_PASSWORD_SALT = "aIf8ObrvtSTkIIGd"
SECURITY_POST_LOGIN_VIEW = "/admin"
SECURITY_PASSWORD_HASH = 'sha256_crypt'
SECURITY_URL_PREFIX = '/user'
SECURITY_CHANGEABLE = True
SECURITY_SEND_PASSWORD_CHANGE_EMAIL = False
SECURITY_CONFIRMABLE = False
SECURITY_REGISTERABLE = False
SECURITY_RECOVERABLE = False
SECURITY_TRACKABLE = True
SECURITY_EMAIL_SENDER = "accounts@flask-diamond.org"
  • SECURITY_PASSWORD_SALT: The salt is a random string you generated during scaffolding. This is used to encrypt the password database.
  • SECURITY_POST_LOGIN_VIEW: the name of the view to redirect to upon a successful login
  • SECURITY_PASSWORD_HASH: the name of the hashing algorithm to use for passwords. sha256_crypt is recommended.
  • SECURITY_URL_PREFIX: Change the URL prefix to make all account-related facilities appear as a subdirectory (like /user).
  • SECURITY_CHANGEABLE: Can users change their own passwords?
  • SECURITY_SEND_PASSWORD_CHANGE_EMAIL: Should users be notified by email when their password is changed?
  • SECURITY_CONFIRMABLE: Must users confirm their email address in order to activate their account?
  • SECURITY_REGISTERABLE: Is self-registration allowed?
  • SECURITY_RECOVERABLE: Can a user reset their password if they have forgotten it?
  • SECURITY_TRACKABLE: Does the User model include fields for recording User account history? By default, Flask-Diamond provides these fields. See the Flask-Security docs for more information about this.
  • SECURITY_EMAIL_SENDER: What is the email address that security messages should be sent from?
ReCAPTCHA

Flask-Captcha provides a quick mechanism for ensuring your application is used by people instead of bots. You may recognize CAPTCHA as the squiggly letters and numbers that you must type into a text box. In order to get started with CAPTCHA and ReCAPTCHA, you must create a free account with their service.

RECAPTCHA_PUBLIC_KEY = '0000_00000000000000000000000000000000000'
RECAPTCHA_PRIVATE_KEY = '0000_00000000000000000000000000000000000'
  • RECAPTCHA_PUBLIC_KEY: The ReCAPTCHA online service will provide you with a public key, which will be included with your web application.
  • RECAPTCHA_PRIVATE_KEY: ReCAPTCHA also provides a private key, but this one must be kept secret. You will enter it in this configuration file, but nowhere else.
Flask-Mail

The simplest way for your application to send email is using Flask-Mail, which makes it pretty easy to create and send emails.

MAIL_SERVER = '127.0.0.1'
MAIL_PORT = 25
MAIL_USE_TLS = False
MAIL_USERNAME = None
MAIL_PASSWORD = None
  • MAIL_SERVER: the hostname or IP address of your SMTP server
  • MAIL_PORT: the port used by your SMTP server. Usually, this is 25 or 465.
  • MAIL_USE_TLS: If the server supports or requires encryption (with TLS), then set this to True
  • MAIL_USERNAME: If you must provide authentication information to your server in order to send email through it, then provide the username here.
  • MAIL_PASSWORD: As with the username, provide the password here if it is required.
Celery

Celery is a job queue that has been integrated into Flask-Diamond so that you create background tasks for any operations that take a while to complete. Typically, you will want your application to respond to requests within 100ms, but when this is not possible, you can achieve a rapid response by queueing the slow operation so that it executes separately. This way, it is still possible to respond to requests quickly enough that nobody will notice.

CELERY_BROKER_URL = 'sqla+sqlite:///var/db/celerydb.sqlite'
CELERY_RESULT_BACKEND = 'db+sqlite:///var/db/results.sqlite'
  • CELERY_BROKER_URL: the URL pointing to a database connection. This is like the SQLAlchemy URI, but different enough that you should consult the documentation.
  • CELERY_RESULT_BACKEND: Celery is able to store job results in a separate database, and for certain types of jobs, this is recommended. The URI here is similar to but different from the CELERY_BROKER_URL.

Makefile Explanation

The Makefile that ships with Flask-Diamond by default includes a number of targets that address several common tasks throughout the life cycle of a project. The way to use the Makefile is with the make command. Thus, to install the project with make, you’d invoke make install.

During development, the Makefile is one of the primary ways for you to interact with your project. You may find yourself running make db server or perhaps make single with some regularity. It is recommended to become familiar with the Flask-Diamond Makefile.

Integration

These targets control project builds.

  • install: Use setup.py to install the project (typically inside a virtualenv).
  • clean: Delete all of the temporary files created by install.
  • docs: Use Sphinx to render the documentation in etc/sphinx.

Development

These targets are used to run the application with the dev.conf profile.

  • server: Invoke the HTTP server in debug mode with dev.conf
  • shell: Enter a python (or ipython) shell within the virtualenv.
  • notebook: Use ipython notebook, if installed, to inspect the application.

Testing

These targets are used to run automated tests.

  • test: Run all of the tests using nosetests.
  • single: Run only tests marked with the @attr("single") decorator.
  • watch: Enter a loop that watches for any project source code to be changed, and then automatically run any tests marked with the @attr("single") decorator.

Databases

These targets control the database. By default these use the dev.conf profile so as to avoid inadvertently changing the production database.

  • db: drop the database, re-create the database, and populate the database with starter values.
  • migratedb: when the data model schema has changed, use migratedb to create a new data migration.
  • upgradedb: apply all data migrations, in order, until the database is up to date.

manage.py Explanation

Many applications will want to expose some functionality through a command line interface, and bin/manage.py provides an easy way to accomplish this. For example:

  • certain administrative tasks could be triggered from the command line
  • other tasks can be automated using a tool like cron

Many of the targets within the Flask-Diamond Makefile are actually wrappers for manage.py, but you can invoke it manually on the command line like so:

SETTINGS=$PWD/etc/conf/dev.conf bin/manage.py runserver

The following commands come with Flask-Diamond by default.

Commands

  • shell: Launch the Python REPL (or iPython if installed) using Flask-Script. By default, the following objects will be imported into the namespace:

    • app: your app’s Flask-Diamond object
    • db: your app’s database object
    • model: your app’s model
  • runserver: Launch your application’s HTTP server. When runserver is invoked, it will bind to localhost. The PORT your application listens on is defined in the Configuration Explanation.

  • publicserver: Like runserver but public. This causes the server to bind to 0.0.0.0 so that remote hosts can connect to your application. This is intended for development purposes, and is not recommended for deployment. See Web Services with WSGI for more information about running a public web service.

  • db: This command acts as the entry point for Flask-Migrate. The subcommands available, taken directly from the command output, are:

    • upgrade: Upgrade to a later version
    • heads: Show current available heads in the script directory
    • show: Show the revision denoted by the given symbol.
    • migrate: Alias for ‘revision –autogenerate’
    • stamp: ‘stamp’ the revision table with the given revision; don’t run any migrations
    • current: Display the current revision for each database.
    • merge: Merge two revisions together. Creates a new migration file
    • init: Generates a new migration
    • downgrade: Revert to a previous version
    • branches: Show current branch points
    • history: List changeset scripts in chronological order.
    • revision: Create a new revision file.
  • useradd: Add a user to the users database via Flask-Security. This accepts the following arguments:

    • email: (required)
    • password: (required)
    • admin: True/False Should this user have the Admin role?
  • userdel: Delete a user from the users database.

  • init_db: Drop the existing database using Flask-SQLAlchemy and re-create it. This obviously destroys anything in the database, resetting it to its original state.

  • populate_db: Sometimes, it is convenient to populate the database with a starting set of objects. The populate_db command is a handy opportunity to ship some data with your project.

Web Services with WSGI

For deploying your application in a production environment, you will probably end up using a WSGI application server like uwsgi or gunicorn. By default, Flask-Diamond will install gunicorn as a requirement.

System Start-up

Under most circumstances, you will want to automatically run the application server when the host boots. This is going to be different for every host, but an example demonstrates launching gunicorn via Ubuntu upstart:

#!upstart
description "flask-diamond daemon"

env USER=flask-diamond
env SETTINGS=/etc/flask-diamond.conf

start on runlevel [2345]
stop on runlevel [06]

respawn

exec start-stop-daemon --start \
    --make-pidfile \
    --pidfile /var/run/$USER-daemon.pid \
    --chuid $USER \
    --exec /var/lib/$USER/.virtualenvs/$USER/bin/gunicorn -- \
        --workers 2 \
        --bind 0.0.0.0:5000 \
        --user $USER \
        --chdir /var/lib/$USER \
        --log-file /var/lib/$USER/gunicorn-error.log \
        --access-logfile /var/lib/$USER/gunicorn-access.log \
        --pid /var/run/$USER-daemon.pid \
        --daemon \
        flask_diamond.wsgi:app

This demonstrates several important principles:

  • setting the configuration file that will control the application server
  • launching the application server from inside the Python virtual environment

Reverse Proxy

You probably want to keep your application server behind a firewall, so a common pattern for deploying Flask-Diamond applications relies upon a reverse proxy. There is a good Digital Ocean tutorial for setting up a reverse proxy with either nginx or apache.

Puppet-Diamond

For scaling up deployment, it is recommended to use an automation solution like Puppet or Chef. Flask-Diamond is particularly easy to deploy with Puppet-Diamond, which will simplify the management of the host configurations as well as application deployment.

Embedded Server

Flask-Diamond provides an embedded web server with bin/manage.py and bin/runserver.py for simple deployments, like development and debugging. This is not recommended for production use. However, when paired with a reverse proxy, this is actually good enough to handle a surprising amount of traffic.

IT Operations with Fabric

There is frequently a split between the application development environment and the live deployment environment. When the application ends up running on a remote host while you are doing your work on a workstation, it can be helpful to simplify remote system operations. Flask-Diamond uses Fabric to make it pretty easy to invoke system functions from the command line.

Using Fabric

To use the Flask-Diamond Fabric functionality, navigate to the root directory of the project and issue the following command:

fab help

This will list all of the available commands.

Flask-Diamond Fabric Commands

By default, Flask-Diamond provides a fabfile.py with the scaffold with the following functionality:

  • rsync: copy files directly from the working directory to the remote host
  • pull: on the remote host, execute git pull in the application directory
  • setup: run make install on the remote host
  • ipython: enter an ipython shell on the remote host
  • shell: open a bash shell on the remote host
  • restart: restart the remote application server
  • nginx_restart: restert the remote web server
  • logs: view the application logs

Customizing Fabric

Because all systems are different, it is not too likely that all of the commands in fabfily.py will work. However, this at least provides a starting point.

About Flask-Diamond

Libraries Included in Flask-Diamond

Flask-Diamond pulls from many different Flask extensions, which are all listed in this document. A significant factor for inclusion with Flask-Diamond is the existence of good documentation. Most of the following libraries therefore provide extensive documentation that can help you to understand everything in greater detail.

Database/Model

  • Flask-SQLAlchemy: “Flask-SQLAlchemy is an extension for Flask that adds support for SQLAlchemy to your application. It requires SQLAlchemy 0.6 or higher. It aims to simplify using SQLAlchemy with Flask by providing useful defaults and extra helpers that make it easier to accomplish common tasks.”
  • Flask-Migrate: “Flask-Migrate is an extension that handles SQLAlchemy database migrations for Flask applications using Alembic. The database operations are provided as command line arguments for Flask-Script.”
  • Flask-Marshmallow: “Flask-Marshmallow is a thin integration layer for Flask (a Python web framework) and marshmallow (an object serialization/deserialization library) that adds additional features to marshmallow, including URL and Hyperlinks fields for HATEOAS-ready APIs. It also (optionally) integrates with Flask-SQLAlchemy.”

Development

  • Flask-Testing: “The Flask-Testing extension provides unit testing utilities for Flask.”
  • Flask-DebugToolbar: “This extension adds a toolbar overlay to Flask applications containing useful information for debugging.”
  • Flask-DbShell: “The extension provides facilites for implementing Django-like ./manage.py dbshell command”
  • Flask-Script: “The Flask-Script extension provides support for writing external scripts in Flask. This includes running a development server, a customised Python shell, scripts to set up your database, cronjobs, and other command-line tasks that belong outside the web application itself.”

Security

  • Flask-Security: “Flask-Security allows you to quickly add common security mechanisms to your Flask application.”
  • Flask-Login: “Flask-Login provides user session management for Flask. It handles the common tasks of logging in, logging out, and remembering your users’ sessions over extended periods of time.”

Doing Stuff

  • Flask-Celery-Helper: “Celery support for Flask without breaking PyCharm inspections.”
  • Flask-Mail: “The Flask-Mail extension provides a simple interface to set up SMTP with your Flask application and to send messages from your views and scripts.”

Interfaces

  • Flask-Admin: “In a world of micro-services and APIs, Flask-Admin solves the boring problem of building an admin interface on top of an existing data model. With little effort, it lets you manage your web service’s data through a user-friendly interface.”
  • Flask-RESTful: “Flask-RESTful is an extension for Flask that adds support for quickly building REST APIs. It is a lightweight abstraction that works with your existing ORM/libraries. Flask-RESTful encourages best practices with minimal setup. If you are familiar with Flask, Flask-RESTful should be easy to pick up.”
  • Flask-WTF: “Flask-WTF offers simple integration with WTForms.”

Presentation

  • Flask-Assets: “Flask-Assets helps you to integrate webassets into your Flask application.”
  • Flask-Markdown: “Flask-Markdown adds support for Markdown to your Flask application. There is little to no documentation for it, but it works just the same as markdown would normally.”

Diagram of Libraries

Flask-Diamond is composed of many other libraries. The following diagram is an effort to describe how these modules are organized. The SVG diagram of Flask-Diamond libraries is also available for download.

_images/flask-diamond_libraries.png

How to Contribute to the Project

Flask-Diamond welcomes contributions from everybody, and there are several ways you can help! The first step is always to clone the project repository using git. If you haven’t done this yet, then do it now. Then, give the rest of this document a read for some specific ideas about contributing.

Implement your favourite features

If you know a feature you’d like to code in order to help with Flask-Diamond, then the easiest way to help is by submitting a pull request to Flask-Diamond on GitHub. From your perspective, this requires no commitment and there is no barrier preventing you from contributing to Flask-Diamond today.

Help with the documentation

Project documentation is one of the most important aspects of an open source project. You can help immensely by editing the current documentation for clarity. You can find the documentation in the Flask-Diamond repository, which you can modify by submitting a pull request.

Help with an existing issue

Another way you can contribute is by working directly upon issues that have been submitted by the community using the Issue Tracker. This is more advanced than simply implementing a new feature, because the issue may specify acceptance criteria. It is recommended that you coordinate with project members before working on an issue. The easiest way to contact the team is through the Issue Tracker itself, because each issue has a comment thread associated with it.

Become a project developer

If you have proven yourself to consistently deliver great work on Flask-Diamond, then you should join the development team! The team uses a Project Kanban to coordinate its development efforts. In order to join the team, you must be familiar with Agile-Diamond, which is the project management framework used for working on Flask-Diamond. Other than that, you must simply open an issue in the Issue Tracker requesting to become a team member.

On rejection...

It should be stated up-front: not every pull request can be merged. We still have to review everything, and sometimes, we cannot accept a pull request. To whatever extent it’s possible, we will explain using the comment features of GitHub. There is an art to creating masterful pull requests, and sometimes beginners have to learn more before they can be good at it. We will help you, but you must realize that this becomes a cost to the team. Please do not take it personally; we were all beginners at some point!

Philosophy of Flask-Diamond

Flask-Diamond provides a path that can guide your thought and development. Flask-Diamond is the road that leads to other ideas.

The following principles serve to guide the development of Flask-Diamond.

Inheritance

Flask-Diamond is primarily configured via Pythonic Inheritance. Your main application object will inherit from Flask-Diamond, and any functions you wish to customize must be overridden in order to change their behavior.

Economy

There are tons of libraries wrapped up in Flask-Diamond. If something has been done well by somebody else, then it is always preferable to leverage that work instead of re-building an unnecessary component.

Stable

Flask-Diamond versions are tied closely to specific versions of third-party libraries. When you stick with a specific version of Flask-Diamond, you can lock the version of third-party libraries too. The goal is to prevent any requirements from changing accidentally so that your application will be more resistant to code rot and API breakage.

Decomposable

Because third-party libraries are constantly changing, it is sometimes desirable to upgrade just one library without upgrading anything else. Flask-Diamond is built atop the Flask ecosystem, which is architecturally decomposable. Thus, extensions may be upgraded individually.

Data-centric

Flask-Diamond was originally built to support the research objectives of Ian Dennis Miller. Through his work on memes and social networks, Ian regularly needed to build lightweight API access to big data sets. Due to the precise nature of the SQL queries needed, a powerful standalone ORM like SQLAlchemy was preferred over the Django approach of bundling the ORM with the web platform. Thus, Flask-Diamond is suitable for projects that require raw access to SQL queries.

Sensible

Flask-Diamond is pretty sensible about the defaults it presents. A lot of decisions have already been made in the service of delivering a functioning application out-of-the-box. The goal is to enable a focus upon the unique parts of your application, rather than the common tasks that most applications need anyway.

Change Log

This file contains a summary of major changes to Flask-Diamond.

0.3.0

2016-01-18

  • a new application startup procedure based on extensions
  • scaffolding files are inside the package now

0.2.15

2015-08-05

  • switch theme to be flask-esque
  • adding new documentation stubs and restructuring TOC
  • added build details to footer
  • created quick-start
  • stubbed several new documents
  • gather git hash using a different command
  • wrote scaffolding explanation
  • wrote philosophy and some of the learning section
  • starting GUIs with Flask-Admin
  • remove sqlite from requirements for documentation build
  • separate requirements from installation
  • remove pysqlite2 requirement
  • added relationship examples to models, rounded out gui examples
  • finishing Views documentation
  • update migration process

0.2.13

2015-07-30

  • controlling documentation more closely
  • migrating markdown documentation to sphinx
  • inter-linking github, pypi, and readthedocs
  • add resources to REST api before calling init_app

0.2.12

2015-07-30

  • This release was used to debug packaging and documentation.

0.2.11

2015-07-30

  • This release was used to debug packaging and documentation.

0.2.10

2015-07-29

  • separate models into submodules
  • remove backref on user roles to permit easier inheritance and overloading of the User model
  • store requirements in separate file
  • split documentation into smaller files

0.2.9

2015-07-08

  • admin views can be turned off
  • admin views can be toggled
  • Create Dependencies.md

0.2.8

2015-06-01

  • Update manage.py

0.2.7

2015-05-13

  • include marshmallow mixin
  • loads() from unmarshalled data
  • load(), loads(), loadf()

0.2.6

2015-04-24

  • hardcoding alembic because the latest version does not parse correctly in FlaskMigrate
  • can disable admin views

0.2.5

2015-03-20

  • useradd and userdel
  • migrate conf files into subdir
  • decent isolation of blueprints, but weirdness with security

0.2.4

2015-03-15

  • bump flask-admin version
  • fixed user create with password
  • fixed layout of login page

0.2.3

2015-03-03

  • mrbob

0.2.2

2015-03-03

  • bump requirements
  • reduce required libraries

0.2.1

2015-02-17

  • delayed commit in CRUD
  • default repr in CRUD
  • bump flask script and SQLAlchemy

0.2.0

2015-02-07

  • use latest Flask-Migrate==1.3.0
  • move user management into user model
  • remove unnecessary variables
  • reorganize
  • meta script helps keep skels aligned
  • trying to get migrations neat
  • working meta-build
  • simpler test fixture
  • using relative paths
  • scaffolding util
  • repair manifest
  • fixing paths for databases
  • tweak documentation
  • automatically sync github pages with API documentation
  • API more prominent
  • autosync documentation
  • include description in sphinx main document
  • documented every method

0.1.10

2015-02-04

  • freeze versions of other dependencies
  • update docs

0.1.9

2015-01-25

  • PEP8 for setup, migrate a few Flask libraries into the core

0.1.8

2014-11-19

  • it is possible to contol the AdminIndexView during app creation

0.1.7

2014-06-29

  • use new class instantiation for flask-mail

0.1.6

2014-06-23

  • remove ipython dependency

0.1.5

2014-06-16

  • more robust user creation
  • admin object local to entire package
  • update flask-admin dependency

0.1.3

2014-03-29

  • do not require a specific version of distribute
  • include webassets

0.1.2

2014-03-22

  • correct auth mixin ordering
  • load/save mixins

0.1.1

2014-03-20

  • split error handlers and request handlers
  • support changeable passwords
  • removed hardcoded config options
  • code annotation
  • steps towards PEP8
  • following Flask capitalization conventions
  • account functions are behind /user URL
  • CRUD create() may defer commit

0.1

2014-03-06

  • Initial public release.

License

The MIT License (MIT)

Flask-Diamond Copyright (c) 2016 Ian Dennis Miller

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

API Reference

API

This documentation is for Flask-Diamond 0.3.0.

Diamond object

class flask_diamond.Diamond(app=None)[source]

Bases: object

A Diamond application.

Parameters:app (Flask) – a Flask app that you created on your own
Returns:None
init_app(name=None, extensions=['configuration', 'logs', 'database', 'accounts', 'blueprints', 'signals', 'forms', 'error_handlers', 'request_handlers', 'administration', 'rest', 'webassets', 'email', 'debugger', 'task_queue'])[source]

Initialize a Diamond application.

Parameters:app (Flask) – a Flask app that you created on your own
Returns:Flask – the initialized application object

This function is the backbone of every Diamond application. It will initialize every component of the application. To control the behaviour of the initialization process, override these functions within your own application.

super(extension_name, **kwargs)[source]

invoke the initialization method for the superclass

ex: self.super(“administration”)

teardown(exception)[source]

Remove any persistent connections during application context teardown.

Returns:None
flask_diamond.create_app()[source]

models

models.user
class flask_diamond.models.user.User(**kwargs)[source]

Bases: flask_sqlalchemy.Model, flask_security.core.UserMixin, flask_diamond.mixins.crud.CRUDMixin, flask_diamond.mixins.marshmallow.MarshmallowMixin

active

boolean – whether the user account is active

add_role(role_name)[source]

update a User account so that it includes a new Role

Parameters:role_name (string) – the name of the Role to add
classmethod add_system_users()[source]

Create a basic set of users and roles

Returns:None
confirm()[source]

update a User account so that login is permitted

Returns:None
confirmed_at

datetime – when the user account was confirmed

current_login_at

datetime – the time of the current login, if any

current_login_ip

string – the IP address of the current login

email

string – email address

id

integer – primary key

last_login_at

datetime – the time of the most recent login

last_login_ip

string – the IP address of the previous login

login_count

integer – the number of times this account been accessed

password

password – the users’s password

classmethod register(email, password, confirmed=False, roles=None)[source]

Create a new user account.

Parameters:
  • email (string) – the email address used to identify the account
  • password (string) – the plaintext password for the account
  • confirmed (boolean) – whether to confirm the account immediately
  • roles (list(string)) – a list containing the names of the Roles for this User
classmethod rm_system_users()[source]

remove default system users

Returns:None
roles
flask_diamond.models.user.roles_users = Table('roles_users', MetaData(bind=None), Column('user_id', Integer(), ForeignKey('user.id'), table=<roles_users>), Column('role_id', Integer(), ForeignKey('role.id'), table=<roles_users>), schema=None)

A secondary table is used for the one-to-many relationship: User has many Roles

models.role
class flask_diamond.models.role.Role(**kwargs)[source]

Bases: flask_sqlalchemy.Model, flask_security.core.RoleMixin, flask_diamond.mixins.crud.CRUDMixin, flask_diamond.mixins.marshmallow.MarshmallowMixin

For the purpose of access controls, Roles can be used to create collections of users and give them permissions as a group.

description

string – a sentence describing the role

id

integer – primary key

name

string – what the role is called

mixins

mixins.crud
class flask_diamond.mixins.crud.CRUDMixin[source]

Bases: object

Convenience functions for CRUD operations.

Adapted from flask-kit.

classmethod create(_commit=True, **kwargs)[source]

Create a new object.

Parameters:
  • commit (boolean) – whether to commit the change immediately to the database
  • kwargs (dict) – parameters corresponding to the new values
Returns:

the object that was created

delete(_commit=True)[source]

Delete this object.

Parameters:commit (boolean) – whether to commit the change immediately to the database
Returns:whether the delete was successful
classmethod find(**kwargs)[source]

Find an object in the database with certain properties.

Parameters:kwargs (dict) – the values of the object to find
Returns:the object that was found, or else None
classmethod find_or_create(_commit=True, **kwargs)[source]

Find an object or, if it does not exist, create it.

Parameters:kwargs (dict) – the values of the object to find or create
Returns:the object that was created
classmethod get_by_id(id)[source]

Retrieve an object of this class from the database.

Parameters:id (integer) – the id of the object to be retrieved
Returns:the object that was retrieved
save(_commit=True)[source]

Save this object to the database.

Parameters:commit (boolean) – whether to commit the change immediately to the database
Returns:the object that was saved
update(_commit=True, **kwargs)[source]

Update this object with new values.

Parameters:
  • commit (boolean) – whether to commit the change immediately to the database
  • kwargs (dict) – parameters corresponding to the new values
Returns:

the object that was updated

mixins.marshmallow
class flask_diamond.mixins.marshmallow.MarshmallowMixin[source]

Bases: object

dump()[source]

serialize the Model object as a python object

classmethod dump_all()[source]

write all objects of Model class to an array of python objects

dumpf(file_handle)[source]

write a Model object to file_handle as a JSON string

classmethod dumpf_all(file_handle)[source]

write all objects of Model class to file_handle as JSON

dumps()[source]

serialize the Model object as a JSON string

classmethod dumps_all()[source]

write all objects of Model class to a JSON-encoded array

classmethod load(python_obj)[source]

create a Model object from a python object

classmethod load_all(python_objects)[source]

create objects of Model class from an array of python objects

classmethod loadf(file_handle)[source]

create a Model object from a file_handle pointing to a JSON file

classmethod loadf_all(file_handle)[source]

create objects of Model class from a file containing an array of JSON-encoded objects

classmethod loads(buf)[source]

create a Model object from a JSON-encoded string

classmethod loads_all(buf)[source]

create objects of Model class from a string containing an array of JSON-encoded objects

mixins.testing
class flask_diamond.mixins.testing.DiamondTestCaseMixin(methodName='runTest')[source]

Bases: flask_testing.utils.TestCase

create_app()[source]

Create a Flask-Diamond app for testing.

setUp()[source]

Prepare for a test case.

tearDown()[source]

Clean up after a test case.

ext

ext.accounts
flask_diamond.ext.accounts.init_accounts(self, app_models=None)[source]

Initialize Security for application.

Parameters:kwargs (dict) – parameters that will be passed through to Flask-Security
Returns:None

A number of common User account operations are provided by Flask- Security. This function is responsible for associating User models in the database with the Security object.

In case you need to override a Flask-Security form (as is the case with implementing CAPTCHA) then you must use super() from within your application and provide any arguments destined for Flask-Security.

>>> def ext_security(self):
>>>    super(MyApp, self).ext_security(confirm_register_form=CaptchaRegisterForm)
ext.administration
flask_diamond.ext.administration.init_administration(self, index_view=None, app_models=None)[source]

Initialize the Administrative GUI.

Parameters:index_view (AdminIndexView) – the View that will act as the index page of the admin GUI.
Returns:None

The administration GUI is substantially derived from Flask-Admin. When this function is called, it will instantiate blueprints so the application serves the admin GUI via the URL http://localhost/admin.

Typically, you will want to call this function even if you override it. The following example illustrates using super() to invoke this administration() function from within your own application.

>>> admin = super(MyApp, self).administration(
>>>     index_view=MyApp.modelviews.RedirectView(name="Home")
>>> )
ext.blueprints
flask_diamond.ext.blueprints.init_blueprints(self)[source]

Initialize blueprints.

Returns:None

By default, this function does nothing. Your application needs to overload this function in order to implement your View functionality. More information about blueprints can be found in the Flask documentation.

ext.configuration
flask_diamond.ext.configuration.init_configuration(self)[source]

Load the application configuration from the SETTINGS environment variable.

Returns:None

SETTINGS must contain a filename that points to the configuration file.

ext.database
flask_diamond.ext.database.init_database(self)[source]

Initialize database

Returns:None

Flask-Diamond assumes you are modelling your solution using an Entity- Relationship framework, and that the application will use a relational database (e.g. MySQL, Postgres, or SQlite3) for model persistence. Thus, SQLAlchemy and Flask- SQLalchemy are used for database operations.

Typically, this just works as long as SQLALCHEMY_DATABASE_URI is set correctly in the application configuration.

ext.debugger
flask_diamond.ext.debugger.init_debugger(self)[source]

Initialize the DebugToolbar

Returns:None

The DebugToolbar is a handy utility for debugging your application during development.

This function obeys the DEBUG_TOOLBAR configuration setting. Only if this value is explicitly set to True will the Debug Toolbarr run.

ext.email
flask_diamond.ext.email.init_email(self)[source]

Initialize email facilities.

Returns:None

Flask-Mail is a useful tool for creating and sending emails from within a Flask application. There are a number of configuration settings beginning with MAIL_ that permit control over the SMTP credentials used to send email.

ext.forms
flask_diamond.ext.forms.add_helpers(app)[source]

Create any Jinja2 helpers needed.

flask_diamond.ext.forms.init_forms(self)[source]

WTForms helpers

Returns:None

WTForms is a great library for using forms and Flask-WTF provides good integration with it. WTForms helpers enable you to add custom filters and other custom behaviours.

ext.handlers
flask_diamond.ext.handlers.init_error_handlers(self)[source]

Initialize handlers for HTTP error events

Returns:None

Flask is able to respond to HTTP error codes with custom behaviours. By default, it will redirect error 403 (forbidden) to the login page.

flask_diamond.ext.handlers.init_request_handlers(self)[source]

request handlers

Returns:None

Flask handles requests for URLs by scanning the URL path. Typically, any serious functionality will be collected into Views. However, this function is a chance to define a few simple utility URLs.

If in your application you want to disable the default handlers in Flask-Diamond, you can override them like this.

>>> def request_handlers(self):
>>>     pass
ext.logs
flask_diamond.ext.logs.init_logs(self)[source]

Initialize a log file to collect messages.

Returns:None

This file may be written to using

>>> flask.current_app.logger.info("message")
ext.marshalling
flask_diamond.ext.marshalling.init_marshalling(self)[source]

Initialize Marshmallow.

Returns:None

By default, this function does nothing.

ext.rest
flask_diamond.ext.rest.init_rest(self, api_map=None)[source]

Initialize REST API.

Returns:None

By default, this function does nothing. Your application needs to overload this function in order to implement your REST API. More information about REST can be found in the documentation.

api_map is an optional function that can be responsible for setting up the API. This is usually accomplished with a series of add_resource() invocations. api_map must take one parameter, which is the Flask-Restful object managed by Flask-Diamond.

You will end up writing something like this in your application:

def api_map(rest):
rest_api.add_resource(Blah)
ext.signals
flask_diamond.ext.signals.init_signals(self)[source]

Initialize Flask signal handlers

Returns:None

Flask provides a number of signals corresponding to things that happen during the operation of the application, which can also be thought of as events. It is possible to create signal handlers that will respond to these events with some behaviour.

ext.task_queue
flask_diamond.ext.task_queue.init_task_queue(self)[source]

Initialize celery.

ext.webassets
flask_diamond.ext.webassets.init_webassets(self)[source]

Initialize web assets.

Returns:None

webassets make it simpler to process and bundle CSS and Javascript assets. This can be baked into a Flask application using Flask-Assets

utils

flask_diamond.utils.flatten(d, parent_key='')[source]

Flatten nested python dictionaries by compressing keys

Parameters:
  • d (dict) – the hierarchical dictionary to flatten
  • parent_key (string) – a prefix to apply to new keys (may be ‘’)
flask_diamond.utils.id_generator(size=8, chars=None)[source]

Create a random sequence of letters and numbers.

Parameters:
  • size (integer) – the desired length of the sequence
  • chars (string) – the eligible character set to draw from when picking random characters
Returns:

a string with the random sequence