Flask-Diamond is a batteries-included Python Flask framework, sortof like Django but radically decomposable. Flask-Diamond offers some opinions about network information systems that process data. Using Flask-Diamond, you can scaffold a working application with sensible defaults, then easily override those defaults to meet your own goals. Flask-Diamond provides a shared vocabulary that helps teams coordinate as they scale up to develop multiple Flask applications while maintaining good code reuse and learning transfer. Flask-Diamond goes beyond a “project scaffold” by providing a complete architecture and team solution, including documentation, tutorials, and other learning support.
A Flask-Diamond application consists of facets, which are common facilities that many applications eventually need to provide. The facets provided by Flask-Diamond include:
The following steps will create a new Flask-Diamond application.
pip install Flask-Diamond
mkdir my-application
cd my-application
flask-diamond scaffold app
make install docs test db server
Please read the Introduction Documentation to get started.
Start here. These introductory readings will teach you how to install Flask-Diamond and how to read the rest of the Flask-Diamond documentation.
Flask-Diamond installs in a Python environment with virtualenv. Please see the System Requirements for information about installation pre-requisites.
Create a virtualenv for your application and install Flask-Diamond.
mkdir my-application
cd my-application
mkvirtualenv -a . my-application
pip install Flask-Diamond
If any of these steps do not work, review the System Requirements and ensure everything is installed.
Enter the virtual environment and scaffold a new Flask-Diamond application. For this Quick Start, just use the default options.
workon my-application
flask-diamond scaffold app
Test the installation to ensure everything is installed correctly:
make test
Create the application database:
make db
Start the application server:
make server
You now have a server running at http://127.0.0.1:5000. Depending on your OS, the following command will open your application in a browser.
open http://localhost:5000
Login with the following account details:
Now that you have scaffolded your first Flask-Diamond application, you can read about how to Learn Flask-Diamond by using the online documentation and materials. If you prefer to learn by experimenting with a live example, see Planets Tutorial for a hands-on introduction to an working Flask-Diamond application.
The best way to learn Flask-Diamond is to read the developers-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.
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:
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.
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
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
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
Note: Have you done this install successfully? Please share your process as a comment on Issue 8.
Here are a few resources to get you started:
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
source my-diamond-app/bin/activate
pip install Flask-Diamond
Learn how to create a new project. This section introduces the Flask-Diamond philosophy and the first steps for making an application.
The Planets Tutorial will introduce Flask-Diamond by demonstrating a simple web application that manages Planets and Satellites in a database. When you are done with this tutorial, you will understand a powerful pattern for using Flask-Diamond to develop applications. This tutorial assumes you have already installed the System Requirements.
The Planets Tutorial consists of two parts:
Simply follow the tutorial and we’ll install everything, then use it to demonstrate some useful principles about Flask-Diamond applications.
During the tutorial, we will accomplish the following:
The following commands will prepare you to begin the tutorial:
mkdir planets
cd planets
mkvirtualenv -a . planets
pip install Flask-Diamond ipython
flask-diamond scaffold app
For a more detailed explanation of these commands, please see the Quick Start. When you run flask-diamond scaffold app, you will be prompted to answer some questions. Give your application and module the name of planets during scaffolding. For all other questions, you can accept the default answers by pressing Enter for each question.
Now, we’ll use a special scaffold called tutorial-planets for the rest of this tutorial. The tutorial-planets scaffold places some example models and views into the application structure.
flask-diamond scaffold tutorial-planets
make test db server
open http://localhost:5000
Your web browser should now display the planets application. If you ran into problems, please review the Quick Start to ensure you have all the requirements installed and working.
The Planets application comes with two object classes:
We will use these classes to model our solar system.
The first thing to do is enter the application shell. The make shell command enters the application context, connects to the database, and starts an interactive shell that will allow us to interact with our application.
make shell
Enter the following commands to create Earth.
from planets import models
earth = models.Planet.create(name="Earth", mass=100.0)
We have provided two parameters to the Planet.create method: name and mass. These model parameters come from the Planet model class definition, which we will investigate in the next section.
Also create Earth’s lunar body, the Moon.
moon = models.Satellite.create(name="Moon", mass=25.0, planet=earth)
Take note of the additional parameter: planet. The application database now contains the Earth and the Moon.
Using a text editor, inspect the files in planets/models.
The Planet model enables us to capture the name and mass of a planet in the application database. The Satellite model is similar to the Planet model, but it also includes a foreign key relationship so that satellites may belong to planets. See Facet: Database for more about how to write model classes.
Log in to GUI
Using a web browser, connect to the application server in a new tab. If you used the default scaffolding settings, your application server is online at http://localhost:5000/.
First, log in as admin@example.com using randomly generated password. The development password can be recovered from Makefile.
Create Mars
Now that you have logged in, create a new Planet called Mars using the GUI. Choose Admin from the drop-down menu at the top of the screen. Select the Planet model from the menu. Once the Planets List View has loaded, click Create to make a new planet. Use the following values:
Create Phobos
Repeat this process to create a new Satellite using the menus.
However, you will run into trouble when you try to set the planet to Mars. To fix this, open the file models/planet.py. Add a function called __str__ within the Planet class:
def __str__(self):
return self.name
With the string representation function in place, try to create Phobos again. You will now be able to select Mars from the drop-down.
The last step in this tutorial is to look at the most important file of all, __init__.py, which controls every aspect of your application. Using a text editor, inspect the file planets/__init__.py. Flask-Diamond applications mostly follow Flask’s create_app() pattern. If you are not yet familiar with Flask applications, read Writing an Application with Flask-Diamond.
blueprints facet
Take a look at init_blueprints, which registers two blueprints that provide basic administrative functionality to your application. To add new views to your application, you will extend this function to register your own blueprints.
administration facet
Finally, look at init_administration, which adds a ModelView for Planets and Satellites. When you create new models in your application, if you wish to edit those models using the GUI, you will need to add those new models to init_administration.
To recap this tutorial, we covered the following:
These fundamental ideas are common to many applications. Of course, this tutorial is just an introduction. Each of these topics has many more readings that will help you learn to master the facets of your application.
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:
mkdir my-application
cd my-application
mkvirtualenv -a . my-application
pip install Flask-Diamond
flask-diamond scaffold app
make install docs test db server
When you invoke flask-diamond, 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.
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:
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.
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.
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.
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.
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.
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.
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.
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.
There are some steps in the scaffolding process that are automatically generated for you because they involve random processes.
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)))'
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)))'
Example: abc
Flask-Diamond can simplify the creation of testing accounts during development. One of the default accounts has administrative privileges, so a simple password is used to protect the account. This password is just 3 letters long by default, so it should never be used anywhere but during development. To ensure this weak password is not used in production, the Makefile is hardcoded to use the development configuration, only.
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.
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.
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.facets.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 application
if not application:
application = Diamond()
application.facet("configuration")
application.facet("logs")
application.facet("database")
application.facet("marshalling")
application.facet("accounts")
application.facet("blueprints")
application.facet("signals")
application.facet("forms")
application.facet("error_handlers")
application.facet("request_handlers")
application.facet("administration")
# application.facet("rest")
# application.facet("webassets")
# application.facet("email")
# application.facet("debugger")
# application.facet("task_queue")
return application.app
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.
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 enable REST functionality, omit email initialization from the extensions passed to create_app(). 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
Flask-Diamond applications initialize the following facets during startup:
See Application Facets for an overview of the specific facets that ship with Flask-Diamond.
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!
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(). |
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.
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.
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.
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.
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.
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.
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.
Learn common development patterns.
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:
This document presents an overview of Model-View-Controller and links to more detailed documentation that discusses these ideas in greater detail.
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.
To model a solar system, you’d start with a model of Planets and Satellites, which are the entities we will be dealing with. A planet can have many satellites, so there is a relationship between our entities. Using nothing more than the idea of “Planets” and “Satellites”, you can go a long way towards modeling a solar system.
A complete data model consists of entities and the relationships between those entities.
In Planets Tutorial, a Planet is a an Entity and so is a Satellite. An attribute of a Planet is its “mass”; the mass of a planet is stored in the data model alongside the name of the planet. Since a planet can have a name, name is therefore also an attribute of a Planet. - Relationships: Entities can affect one another through relationships. In tutorial-planet, a Planet can have many Satellites. Since a Planet can have many Satellites, 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 Facet: Database for a more detailed discussion and code examples.
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.
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 Facet: Blueprints for a more detailed discussion and code examples.
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 Facet: Administration 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 Facet: REST 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. |
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:
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 flask.ext.diamond import db
class Planet(db.Model, CRUDMixin):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255))
mass = db.Column(db.Integer)
Notice that class Planet() inherits both db.Model and CRUDMixin. Now the following CRUD workflow is possible:
earth = Planet.create(name="earth", mass=200)
print "{name} has mass of {mass}".format(earth)
earth.update({'name': "Earth"})
print "Its name is {name}".format(myself)
earth.delete()
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 Planet model described above.
from flask_diamond import Diamond
from flask_diamond.administration import AdminModelView
from .models import User, Role, Planet
class my_diamond_app(Diamond):
def init_administration(self):
admin = self.super("administration", user=User, role=Role)
admin.add_view(AdminModelView(
Planet,
db.session,
name="Planet",
category="Admin")
)
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.
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.
A database migration is potentially complex, so creating one takes several steps.
We will work on the dev database, which should not have any important data in it.
grep SQLALCHEMY_DATABASE_URI etc/conf/dev.conf
This works by comparing python model to last migration version
make newmigration
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.
cd ${MODULE}/migrations/versions
mv ${CHECKSUM}_.py ${CHECKSUM}_${SUMMARY}.py
The migration is ready. It can now be applied in the production environment by specifying the configuration for the production system.
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
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 ###
To apply a migration to the development database, enter the virtualenv and run:
make migrate
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.
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
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
In fact, the full functionality of Flask-Migrate is easily available on the command line:
bin/manage.py db help
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. |
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.
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 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.
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.
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.
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.facets.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.
In the project on the command line:
make docs
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.
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.
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.
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”.
It is possible to invoke the testing subsystem from the command line. The following different testing methods are available in Flask-Diamond:
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
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
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.
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.
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.
This section describes the facets of Flask-Diamond.
A Flask-Diamond application consists of many components (called facets) that complement each other to form a coherent application. These facets are common to many applications, but they can be enabled and disabled individually depending on specific requirements. A Flask-Diamond application is customized by changing the way these facets behave.
Here are all of the facets currently shipping with Flask-Diamond:
Now that you know what facets are, learn how to use them by Writing an Application with Flask-Diamond.
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.
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.
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.
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.
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.
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.
The following example imports a class called Planet (which is described in the Model documentation). Then, the Planet class is added to the GUI using the add_view() method.
from flask_diamond import Diamond
from flask_diamond.administration import AdminModelView
from .models import User, Role, Planet
class my_diamond_app(Diamond):
def init_administration(self):
admin = self.super("administration", user=User, role=Role)
admin.add_view(AdminModelView(
Planet,
db.session,
name="Planet",
category="Admin")
)
When the server is launched, it provides a GUI facility for applying CRUD operations to the Planet model. Due to the use of AdminModelView, the application will now require a password before allowing anybody to create, read, update, or delete any Planet object.
The category parameter causes the GUI to put this View into the menu bar beneath the heading “ModelAdmin”. The name parameter indicates this link will be titled Planet. Now you can find the Planet CRUD by navigating through the menu to Models and then to Planet.
BaseModelView actually creates multiple views that provide CRUD functionality:
Create-List-Edit-Delete corresponds directly to Create-Read-Update-Delete.
Flask-Admin makes it pretty easy to add custom functionality through Python class inheritance [1]. In the Model documentation, the Planet Model provides a bombard() method that sends an asteroid at a planet. The following sections demonstrate how to expose the bombard() method and create a user interface widget for calling that method.
In addition to the basic CRUD views, new views can be created for doing other things with Models. Since the Planet class has been extended with a bombard() method, let’s create a URL endpoint to send an asteroid at a planet.
from flask_diamond import Diamond
from flask_diamond.administration import AdminModelView
from flask_admin import expose
from .models import User, Role, Planet
class PlanetModelView(AdminModelView):
@expose('/bombard/<planet_id>')
def bombard(self, planet_id):
the_planet = models.Planet.get(planet_id)
the_planet.bombard(mass=10.0)
return flask.redirect(flask.url_for('.list_view'))
class my_diamond_app(Diamond):
def init_administration(self):
admin = self.super("administration", user=User, role=Role)
admin.add_view(PlanetModelView(
models.Planet,
db.session,
name="Planet",
category="Admin")
)
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 Planet.mass with a “bombard” button:
import jinja2
from flask_diamond import Diamond
from flask_diamond.administration import AdminModelView
from flask_admin import expose
from .models import User, Role, Planet
class PlanetModelView(AdminModelView):
def mass_formatter(self, context, model, name):
mass_widget_template = "{0} <a href='{1}'>bombard!</a>"
mass_widget = mass_widget_template.format(
model.age,
flask.url_for(".bombard", planet_id=model.id)
)
return jinja2.Markup(mass_widget)
column_formatters = {
"age": mass_formatter,
}
When these two PlanetModelView examples are combined, the result is a user interface that can bombard a planet with asteroids when clicked.
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 PlanetAdmin(AuthModelView):
edit_template = 'planet_edit.html'
column_list = ("name", "mass")
form_overrides = {
"upload_buffer": FileUploadField
}
form_args = {
'upload_buffer': {
'label': 'Planet PDF',
'base_path': "/tmp",
}
}
Flask-Admin is really powerful, and the best way to learn more is by reading the Flask-Admin documentation.
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. |
As is explained in MVC: Model, View, Controller, 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.
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.
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).
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. To use Planets Tutorial as an example, we could extend the template to print a special message when Earth is viewed.
{% if planet.name=='earth' %}
Go Earthlings!
{% endif %}
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://example.com"}]}
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.
URLs are used within web applications to identify Views. In Planets Tutorial, the URL /planet_list could return with a summary of all the planets in the database, and a URL like /planet/earth could provide information about the named planet.
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 /planet/earth to view details about a planet called Earth. 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
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. In Planets Tutorial, there could be several views related to tracking moons (satellites), which could be collected into the /satellite 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 Flask documentation also explains how to register a blueprint with an application:
from flask import Flask
from planets.simple_page import simple_page
app = Flask(__name__)
app.register_blueprint(simple_page)
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.
In Flask-Diamond, a Model is a way of reading and writing a database. If our application is a model of the solar system, then we’re modeling planet 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 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 planet with a name and mass. Let us also create a model of a satellite that orbits a planet. The following example demonstrates one way this model might be accomplished. [1]
from flask_diamond import db
from flask_diamond.mixins.crud import CRUDMixin
class Planet(db.Model, CRUDMixin):
"A Planet is a celestial body"
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
mass = db.Column(db.Float())
Now, we can model a Planet and a Satellite. Notice that satellites specify a relationship to a planet. Let’s use our data model to create a few objects in our database.
from planets import models
earth = models.Planet.create(name="Earth", mass=100.0)
mars = models.Planet.create(name="Mars", mass=90.0)
Now let’s assume our planets can be bombarded by asteroids with a certain mass. We can extend our planet model to incorporate this feature
class Planet(db.Model, CRUDMixin):
"A Planet is a celestial body"
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
mass = db.Column(db.Float())
def bombard(self, mass=1.0):
self.mass += mass
self.save()
Now we can send an asteroid at a planet and our data model will add the mass of the asteroid to the planet:
from planets import models
earth = models.Planet.create(name="Earth", mass=100.0)
earth.bombard(mass=15)
print(earth.mass)
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 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.
The SQLAlchemy Basic Relationships document provides an excellent overview of different relationship patterns, including:
To demonstrate a basic relationship, let’s say a planet can have a satellite orbiting it:
from flask_diamond import db
from flask_diamond.mixins.crud import CRUDMixin
class Planet(db.Model, CRUDMixin):
"A Planet is a celestial body"
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
mass = db.Column(db.Float())
class Satellite(db.Model, CRUDMixin):
"A Satellite orbits a Planet"
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
mass = db.Column(db.Float())
planet = db.relationship('Planet', backref=db.backref('satellites', lazy='dynamic'))
planet_id = db.Column(db.Integer(), db.ForeignKey("planet.id"))
The following code example uses the classes above to create a planet called Earth with a moon.
from planets import models
earth = models.Planet.create(name="Earth", mass=100.0)
moon = models.Satellite.create(name="Moon", mass=25.0, planet=earth)
Based on the Planet class, a simple query that finds a planet named “Earth” looks like:
earth = models.Planet.find(name="Earth")
print(earth.mass)
However, the SQLAlchemy Query API is extremely powerful, and its documentation is the authoritative source.
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:
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 to learn about altering a production database.
For the sake of illustration, the following is a recursive model that is able to link to itself, creating a friendship graph of individuals.
from flask_diamond import db
from flask_diamond.mixins.crud import CRUDMixin
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 __str__(self):
return self.name
Footnotes
[1] | Note the use of CRUDMixin, which provides us with a create() method. For more information about CRUDMixin, see CRUD: Create, Read, Update, Delete. |
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.
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.
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.
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().
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.
By default, flask_diamond.facets.email is already enabled in flask_diamond.Diamond.init_app(), so there is nothing special to do. The project created by flask-diamond 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.
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.
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.
Representational State Transfer, usually abbreviated REST, can be simplified as follows:
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.
Let’s say you are building an API for a solar system and you need to manage planets for observation. Our API could then expose one URL endpoint for retrieving all planets: /api/planet_list. We could then add a planet to our solar system with an HTTP POST containing the following data:
{
'name': 'Venus',
'mass': '90.0'
}
Such a POST 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.
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():
from flask_diamond import db
from .mixins import DiamondTestCase
from ..models import Planet
def init_rest():
class PlanetResource(Resource):
def get(self, name):
return models.planet.find(name=name).dumps()
rest.add_resource(PlanetResource, '/api/planet/<str:name>')
This simple example creates a resource called PlanetResource. Then, it specifies a way to handle the GET verb, which will result in retrieving a planet. Finally, the resource is exposed using a URL: /api/planet/....
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.planet.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.
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/rename_planet because that describes an action: changing a planet’s name. 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/rename_planet because that’s actually something you want to push to the server, not get from the server. However, early web developers tried to make this work using arguments, which 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.
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.
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.
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.
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
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
$
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) $
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
By default, Flask-Diamond provides make install, which will use requirements.txt to install your project’s pre-requisites automatically.
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.
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/conf/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/conf/dev.conf bin/manage.py server
To start the server with the production.conf environment:
SETTINGS=$PWD/etc/conf/production.conf bin/manage.py server
Flask-Diamond ships with a few configuration profiles to get you started:
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.
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.
For testing purposes, there is a special configuration that writes to a temporary database that is created and destroyed during tests.
If you inspect the Makefile, you will see that $SETTINGS=$PWD/etc/conf/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.
By default, Flask-Diamond expects the following variables to be present within a configuration file.
The project is configured with the following directives.
PROJECT_NAME = "Flask-Diamond"
PORT = 5000
LOG = "/tmp/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"
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
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"
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'
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
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'
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.
These targets control project builds.
These targets are used to run the application with the dev.conf profile.
These targets are used to run automated tests.
These targets control the database. By default these use the dev.conf profile so as to avoid inadvertently changing the production database.
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:
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.
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.
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.
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:
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.
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.
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.
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.
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.
By default, Flask-Diamond provides a fabfile.py with the scaffold with the following functionality:
Because all systems are different, it is not too likely that all of the commands in fabfile.py will work. However, this at least provides a starting point.
This documentation is for Flask-Diamond 0.5.1.
Bases: flask_sqlalchemy.Model, flask_security.core.UserMixin, flask_diamond.mixins.crud.CRUDMixin, flask_diamond.mixins.marshmallow.MarshmallowMixin
boolean – whether the user account is active
update a User account so that it includes a new Role
Parameters: | role_name (string) – the name of the Role to add |
---|
datetime – when the user account was confirmed
datetime – the time of the current login, if any
string – the IP address of the current login
string – email address
integer – primary key
datetime – the time of the most recent login
string – the IP address of the previous login
integer – the number of times this account been accessed
password – the users’s password
Create a new user account.
Parameters: |
|
---|
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.
string – a sentence describing the role
integer – primary key
string – what the role is called
Convenience functions for CRUD operations.
Adapted from flask-kit.
Create a new object.
Parameters: |
|
---|---|
Returns: | the object that was created |
Delete this object.
Parameters: | commit (boolean) – whether to commit the change immediately to the database |
---|---|
Returns: | whether the delete was successful |
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 |
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 |
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 |
create objects of Model class from an array of python objects
create a Model object from a file_handle pointing to a JSON file
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.
>>> result = self.super("accounts", user=User, role=Role,
>>> confirm_register_form=ExtendedRegisterForm)
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")
>>> )
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.
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.
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.
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.
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.
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
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:
planet = Planet.find(name=name) if planet:
return(planet.dump())
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.
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
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.
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, there is no barrier preventing you from contributing to Flask-Diamond today.
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.
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.
The only requirement for your first pull request that you must add your name to the Contributors List, signifying your acceptance of the Flask-Diamond Contributors Agreement.
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.
Flask-Diamond is a volunteer effort. This page discusses the Contributors Agreement and Contributors List. This page is also the official document for signing up as a contributor to the Flask-Diamond project. To learn more about how you can volunteer, please read How to Contribute to the Project.
Contributors to the Flask-Diamond project affirm:
For the benefit of the Flask-Diamond project:
Contributors to the Flask-Diamond project signify acceptance of the Contributors Agreement by submitting a git pull request appending themselves to the Contributors List.
The MIT License (MIT)
Flask-Diamond
Copyright (c) 2014-2017 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.
This file contains a summary of major changes to Flask-Diamond.
2017-01-04
2016-05-21
2016-05-03
2016-01-21
2015-08-05
2015-07-30
2015-07-29
2015-04-24
2015-03-20
2015-03-15
2015-02-07
2014-06-16
2014-03-20