Have you just gotten started with Python for the web, and are wondering which Python framework to learn or use for your next project? Then you’ve probably heard of Django, Flask, or both. Although there are other Python web frameworks, such as Pyramid, web2py, and TurboGears, Django and Flask remain the most popular.
This will offer an in-depth comparison of Flask and Django, looking at various factors.
Note that this series doesn’t aim to pit the two frameworks against each other and declare an overall winner. I will simply compare and contrast them side-by-side on the aforementioned factors, in the hopes that after reading this article, you’ll be able to make a more informed decision on which framework, if any, to use for a particular project. To follow along in this series, it is expected that you have a basic to intermediate understanding of Python programming.
Flask is a Python microframework with the slogan “web development, one drop at a time”. It was created by Austrian developer Armin Ronacher, and first released on April 1st, 2010. As explained in its documentation foreward, the “micro” in “microframework” means that Flask aims to be as simple as possible, yet still extensible. In this series, I will use Flask 0.12.2.
Django prides itself on being the “web framework for perfectionists with deadlines”. It is developed and maintained by the Django Software Foundation (DSF), and was first released on July 15th, 2005. Being older, it has had more version releases than Flask. Django is free and open source. In this series, I will use Django 1.10.6.
I mentioned earlier that Flask and Django are Python’s most popular web frameworks, but what does that really mean, and how do they compare? Let’s look at the data.
At the time of writing this article, Flask has 31,253 stars and 1,869 watchers onGitHub. It’s been forked 9,864 times.
Django on the other hand, has 29,862 stars, 1,848 watchers, and has been forked 12,599 times.
Based on these stats, the two frameworks are almost neck and neck, all things considered. However, Flask is doing better with stars and watchers, which becomes significant when one considers that Django is 5 years older. As for other Python frameworks, they simply do not come close to Flask and Django on GitHub.
Both frameworks have been used in quite a number of active projects. Some of the well-known projects powered by Django include Pinterest, Disqus, Eventbrite, Instagram and Bitbucket.
Django seems to be used as the main framework powering many applications, whereas Flask is often used just for API’s (such as with Pinterest and Twilio).
Flask provides an extensive documentation, covering everything from installation to deployment, and includes quickstart instructions as well as a more detailed tutorial.
Installing Flask with Python’s package manager, pip, is easy.
$ pip install flask
$ pip freeze
appdirs==1.4.3
click==6.7
Flask==0.12
itsdangerous==0.24
Jinja2==2.9.5
MarkupSafe==1.0
packaging==16.8
pyparsing==2.2.0
six==1.10.0
Werkzeug==0.12.1
The pip install flask
command installs Flask as well as a few other necessary packages, which you can view with the pip freeze
command. They include Jinja2, a template engine, and Werkzeug, a utility library for WSGI.
Getting Flask up and running (for example, a page that displays “Hello World”) is quite simple. All you need to do is create an instance of the Flask
class, and then create a route that displays the “Hello World” string to the homepage:
## app.py
from flask import Flask
app = Flask(**name**)
@app.route('/')
def hello_world():
return 'Hello World!'
All that’s left to do is run the file:
$ export FLASK_APP=app.py
$ flask run
_ Serving Flask app "app"
_ Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
And just like that, you have a running app:
Beginners should however beware, especially when building a more complex app. Because Flask aims to be as minimal as possible, it has no command to autogenerate code. Flask doesn’t make any decisions for you, be it with regards to directory structure or to database systems. Even the templating system it ships with is easy to change. While this offers convenience for experienced developers, it can pose a bit of a challenge for beginners because it is not immediately obvious what goes where. For this reason, I would recommend following an introductory best practices guide such as this one, to enable you to structure and develop your app in a convenient way that’s been tried and tested.
Django’s documentation is more extensive than Flask’s, which is to be expected because Django is not as minimalistic as Flask.
Django, too, can be easily installed using pip. It also comes with a few packages out of the box:
$ pip install django
$ pip freeze
appdirs==1.4.3
Django==1.10.6
packaging==16.8
pyparsing==2.2.0
six==1.10.0
Getting started with Django involves running an in-built command to create a project, and then another command to create an app. First, we’ll create a project called mysite
:
$ django-admin startproject mysite
This creates some files and directories as follows:
└── mysite
├── manage.py
└── mysite
├── **init**.py
├── settings.py
├── urls.py
└── wsgi.py
The files contain some autogenerated code, including settings and a default admin URL route. We can access the default homepage before creating any app. To do so, start the Django server like so:
$ cd mysite
$ python manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
November 27, 2017 - 12:21:45
Django version 1.10.6, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Then navigate to http://127.0.0.1:8000/
. You should see this page:
We can also access the admin login page:
Next we’ll create a Django app.
$ cd mysite
$ python manage.py startapp new_app
This creates a new_app
folder, which has some autogenerated files as seen below:
└── mysite
└── new_app
├── _**init**_.py
├── admin.py
├── apps.py
├── migrations
│ └── _init_.py
├── models.py
├── tests.py
└── views.py
Django’s start-up commands are convenient, especially for beginners, because they create a ready-to-use directory structure, and it is clear what goes where based on the files that have automatically been created. Note that each new app created must be registered in the INSTALLED_APPS
list in the settings.py
file:
## mysite/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
## add new app here
'new_app',
]
Routing is essential for any website or web application, as it creates URLs and determines what is displayed when each URL is loaded.
In Flask, routing is done using a route()
decorator. Let’s take the Hello World
example we saw in the previous section:
## app.py
@app.route('/')
def hello_world():
return 'Hello World!'
The first line is a decorator, that is, a function that takes a callable (a function or a class) as a parameter, modifies it, and returns it. The snippet above can be broken down as follows:
app
in @app.route()
is an instance of the Flask class. This class is imported directly from Flask.route()
decorator takes a string as a parameter. This string tells Flask what URL should trigger the function. In this case, it is simply /
, which refers to the index page, that is, the very first page that loads when you access Flask in your browser ([http://127.0.0.1:5000/]([http://127.0.0.1:5000/](http://127.0.0.1:5000/\))). If we had put a different string, such as '/hello-world'
then we would need to access it at http://127.0.0.1:5000/hello-world.hello_world()
is the function that is triggered by the decorator. In this case, it returns a “Hello World!” string. It can also return a HTML template - but we’ll get to that later!If you attempt to access a URL that has not been defined, you will get a 404 error:
In Django, routing is handled in the urls.py
file, which is one of the files created by running the in-built djangoadmin startproject
command. Defining a route entails importing the url
method from Django and creating an instance of it, specifying the following parameters:
urls
moduleThe urls.py
file contains a urlpatterns
list which consists of url()
instances. In the next section, we will create our own Django view, and then create a new URL pattern for it. In the meantime, let’s look at the URL for the in-built Django admin site, which comes by default in the urls.py
file:
## mysite/urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
]
The instance of url()
above includes the regex r^admin/
, which tells Django that loading the http://127.0.0.1:8000/admin URL should load the URLs specified in the admin.site.urls
module. Note that we import admin
from Django.
Like Flask, attempting to access a non-defined URL in Django will give a 404 error:
For any web application, there must be a front-end, or a user interface, allowing users to interact with the system. The front-end consists of static HTML pages, usually styled with CSS. Since web apps are not static websites, there needs to be a way to generate HTML dynamically. This is where templates come in.
Flask uses Jinja2, a modern and designer-friendly template engine for Python, by default.
So far, our Flask app contains only one file, app.py
. Let’s organize our directory structure before going ahead. First, rename app.py
to run.py
. Next, create a config.py
file in the same directory as run.py
. Then, create an app
directory. In it, create the following files: __init__.py
, and views.py
. Your directory structure should now look like this:
├── app
│ ├── **init**.py
│ └── views.py
├── config.py
└── run.py
The config.py
file is similar to Django’s settings.py
, and contains Flask configurations. Add the following code to it:
## config.py
DEBUG = True
Setting debug
to True enable Flask’s debugging features. Note that is should always be False in production.
Now, let’s work on the app/__init__.py
file. This is where we will initialize the app. Note that setting instance_relative_config
to True, allows us to use app.config.from_object('config')
to load the config.py file.
## app/**init**.py
from flask import Flask
## Initialize the app
app = Flask(**name**, instance_relative_config=True)
## Load the views
from app import views
## Load the config file
app.config.from_object('config')
Now, we will edit our views.py
file. Views in Flask can be based on classes or functions. the hello_world
function we had earlier is an example of a view. Let’s transfer it to the views.py
file, and add a new view to demonstrate templates:
## app/views.py
from flask import render_template
from app import app
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/show-template')
def show_template():
return render_template("template.html")
Lastly, let’s modify the run.py
file, which will be our app’s entry-point:
## run.py
from app import app
if **name** == '**main**':
app.run()
Run the Flask app like so:
$ export FLASK_APP=run.py
$ flask run
_ Serving Flask app "run"
_ Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Now, navigate to http://127.0.0.1:5000/show-template in your browser. You should get an error page due to the fact that template.html
doesn’t exist - yet. Create a app/templates
directory, and in it, we’ll create a very simple HTML template:
<!-- templates/template.html -->
<!DOCTYPE html>
<html>
<body>
<h1>Displaying a Template</h1>
<p>Templates in Flask using Jinja2</p>
</body>
</html>
Run your app and navigate to http://127.0.0.1:5000/show-template again; you should see your template in the browser now:
Now, Jinja2 templates contain variables as well as tags. Below are examples of some commonly used Jinja2 syntax:
{% ... %}
is used for statements. Jinja2 statements can be used in a variety of cases, including for loops
, if-else statements
, macros
(which are similar to functions in regular programming) as well as imports
.{{ ... }}
is used for variables. To pass a variable to a template, we use a context dictionary, which is defined in the view. We’ll cover this in Part Two of the series, under the Views and Forms
section.{## ... #}
is used for to comment out Jinja2 syntax.Let’s see this in action. Open your template file and add the following:
<!-- templates/template.html -->
<!-- add this after the first paragraph -->
<h2> Statements </h2>
{% if my_variable %}
<p>
This paragraph won't be displayed in the template because the variable
"my_variable" does not exist. The if statement therefore returns False.
</p>
{% else %}
<p>
This paragraph will be displayed.
</p>
{% endif %}
<h2> Variables </h2>
<p>{{ my_variable }}</p>
<p>
The variable above will not be displayed because it doesn't exist!
</p>
<h2> Comments </h2>
{% if True %}
<p>
This will be displayed because the if statement returns True.
</p>
{% endif %}
{#
{% if True %}
<p>
This will not be displayed because it is commented out using Jinja2 syntax.
</p>
{% endif %}
#}
This should give the following output:
An important feature of Jinja2 templates is template inheritance. This allows you to define a base template that contains content that would need be duplicated across several web pages, such as a logo, navigation bar, and footer. Subsequent templates can then inherit from the base template, thus eliminating the need to duplicate content across several HTML files. This is done by defining a {% extends "base.html" %}
tag at the beginning of each subsequent template file, assuming that base.html
is the base template.
For more information on Jinja2 templates, take a look at the official documentation.
Fun fact: the Jinja2 template engine was inspired by the Django template language, therefore their syntax is quite similar! Much like Jinja2, the Django templating syntax includes:
{{ ... }}
{% ... %}
{{ variable|filter }}
{## ... #}
for single line comments and {% comment %} ... {% endcomment %}
for multi-line commentsLet’s see this in action. Begin by creating a /templates
directory in your mysite/new_app
directory, and a templates.html
file inside it. Your directory structure should now look like this:
└── mysite
├── manage.py
├── mysite
│ ├── **init**.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── new_app
├── _**init**_.py
├── admin.py
├── apps.py
├── migrations
│ └── _init_.py
├── models.py
├── templates
│ └── template.html
├── tests.py
└── views.py
Add the following to the template.html
file:
<!-- mysite/new_app/templates/template.html -->
<!DOCTYPE html>
<html>
<body>
<h1>Displaying a Template</h1>
<p>Using Django Templates</p>
<h2> Statements </h2>
{% if my_variable %}
<p>
This paragraph won't be displayed in the template because the variable
"my_variable" does not exist. The if statement therefore returns False.
</p>
{% else %}
<p>
This paragraph will be displayed.
</p>
{% endif %}
<h2> Variables </h2>
<p>{{ my_variable }}</p>
<p>
The variable above will not be displayed because it doesn't exist!
</p>
<h2> Comments </h2>
{% if True %}
<p>
This will be displayed because the if statement returns True.
</p>
{% endif %}
{% comment %}
{% if True %}
<p>
This will not be displayed because it is commented out using Django
multi-line comments.
</p>
{% endif %}
{% endcomment %}
{## <p> This is a single-line Django comment. It won't be displayed. </p> #}
</body>
</html>
Next, we’ll implement a simple class-based Django view in our mysite/urls.py
file to render the template:
## mysite/mysite/urls.py
from django.views.generic import TemplateView
## add the show-template URL below the existing admin URL
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^show-template/$', TemplateView.as_view(template_name="template.html")),
]
Start the Django server, and then navigate to http://127.0.0.1:8000/show-template/
. The resulting output will be similar to what we had in our Flask app:
Django templates also has a template inheritance feature, with the base template being inherited with a {% extends "base.html" %}
tag, exactly as is done with Jinja2. For more information about the Django template language, visit the official documentation.
Storing data is an essential part of any web application. There are several things for a developer to consider with regards to storing data. Of particular importance is the type of data to be stored, which informs the type of database that is best suited to store that data. Let’s see what data storage options Flask and Django have.
Flask does not support any particular database out of the box. This allows you to choose what kind of database you’d like to use, be it relational (SQL) or non-relational (NoSQL). The Flask documentation provides an example of how you can use an sqlite database with Flask.
Flask has a number of great extensions (packages that extend Flask’s functionality) that can make database connections much easier. One of the most popular ones is Flask-SQLALchemy, which allows us to use SQLAlchemy with Flask. SQLAlchemy is an Object Relational Mapper (ORM), which means that it connects the objects of an application to tables in a relational database management system.
You can also use Flask with popular NoSQL databases such as MongoDB. Flask-MongoAlchemy is the MongoDB equivalent of Flask-SQLAlchemy, and provides a user-friendly proxy between Flask and MongoDB. An alternative is Flask-PyMongo, which bridges Flask with PyMongo. PyMongo is recommended by MongoDB for Python projects.
Models are classes that represent database tables. It is not always necessary to have models in a Flask app. (However, if going the SQLAlchemy route, models are essential.) Models are convenient because you can include validation for data type, length, required fields, and unique fields. Here’s an example based on Flask-SQLAlchemy. First, install the extension:
$ pip install flask-sqlalchemy
Then, initialize SQLALchemy. Note that you must provide a database URI:
## app/__init__.py
from flask_sqlalchemy import SQLAlchemy
## Initialize SQLAlchemy
app.config.update(dict(
SQLALCHEMY_DATABASE_URI='sqlite:////tmp/database.db' ## path to your database
))
db = SQLAlchemy()
db.init_app(app)
Next, create the model:
## app/models.py
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(60), index=True, unique=True)
username = db.Column(db.String(60), index=True, unique=True)
first_name = db.Column(db.String(60), index=True)
last_name = db.Column(db.String(60), index=True)
When using models, it is important to have a way to manage changes in the code and implement them in the database. This is where migrations come in. If using SQLAlchemy, Alembic is the go-to migration tool as it was created specifically for use with SQLAlchemy. Some extensions which make using Alembic with Flask easier are Flask-Alembic and Flask-Migrate.
Django’s default database engine is sqlite, which is automatically defined in the settings.py
file. Django also creates a default sqlite database named db.sqlite3
, which can be found in the app’s root directory.
## mysite/settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
This can be changed to any other database backend, such as MySQL or PostgreSQL. You can also specify the user, password, hostname, and port, as seen in this example from the Django documentation:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydatabase',
'USER': 'mydatabaseuser',
'PASSWORD': 'mypassword',
'HOST': '127.0.0.1',
'PORT': '5432',
}
}
You can also use Django with NoSQL databases by making use of third-party packages like Django MongoDB Engine.
Models are an essential part of a Django app, so much so that one of the files created by the Django startapp
command is a models.py
file. Here’s an example:
## new_app/models.py
from __future__ import unicode_literals
from django.db import models
## Create your models here.
class User(models.Model):
email = models.EmailField(max_length=60)
username = models.CharField(max_length=60)
first_name = models.CharField(max_length=60)
last_name = models.CharField(max_length=60)
Models in Django allow you to select from various field types ranging from integers to emails and even file fields. You can also specify whether the field can be left blank or whether it must be unique. In Django, there is no need to specify an id
field as there is an auto-incrementing primary key by default for each model. However, you can create your own primary key by adding a field with the primary_key=True
parameter. To learn more about models in Django, visit the models documentation.
Django handles database migrations out of the box without having to install any packages. If changes have been made to the models (or if your database hasn’t been set up with tables yet), simply run the following commands to propagate them to the database:
$ python manage.py makemigrations
$ python manage.py migrate
To learn more aout Django migrations, visit the migrations documentation.
Views hold the logic of a web app, and are most often where the functionality of the app lies. They take in a web request and return a web response. Let’s see how the two frameworks handle views.
In Flask, there are two types of views: function-based views, and pluggable (class-based) views. Function-based views work with Flask’s routing system, which we explored in Part One of this series under the Routing System section. The hello_world()
function we wrote is an example of a function-based view. Another example would be:
## app/views.py
@app.route('/current-time')
def current_time():
now = datetime.datetime.now()
day = now.strftime("%B %d, %Y")
time = now.strftime("%I:%M %p")
return 'It is now ' + time + ' on ' + day + '.'
This view function displays a string that shows the current date and time:
Pluggable views were introduced in Flask 0.7, and are based on classes rather than functions. They are generally more flexible than function-based views. The view class must inherit from the flask.views.View
class and implement the dispatch_request()
method. The class must also include the add_url_rule()
method. This method is used instead of the @app.route()
decorator that is used in function-based views. The following code snippet converts the current_time()
function we wrote earlier to a pluggable view:
## app/views.py
from flask.views import View
class CurrentTimeTwo(View):
def dispatch_request(self):
now = datetime.datetime.now()
day = now.strftime("%B %d, %Y")
time = now.strftime("%I:%M %p")
return 'It is now ' + time + ' on ' + day + '.'
app.add_url_rule('/current-time-two', view_func=CurrentTimeTwo.as_view('current_time_two'))
This displays exactly what the previous view did:
To learn more about pluggable views in Flask, visit the documentation.
Django, too, has both function-based views and class-based views. Here’s an example of the view we wrote in Flask, as a function-based Django view:
## new_app/views.py
def current_time(request):
now = datetime.datetime.now()
day = now.strftime("%B %d, %Y")
time = now.strftime("%I:%M %p")
return HttpResponse("It is now " + time + " on " + day + ".")
Once a view is written, it can then be mapped to a URL by including it in the urls.py
file. Add the following to the urlpatterns
list:
## mysite/urls.py
from new_app import views
urlpatterns = [
url(r'^current-time/$', views.current_time)
]
This is displayed as:
Class-based views in Django usually include different methods within the class for different HTTP methods, such as GET and POST. They also help make code more reusable by implementing OOP techniques like inheritance. Making the current_time()
function a class-based view is quite straighforward. Note that only the get()
method is defined here:
## new_app/views.py
from django.views import View
class CurrentTimeTwo(View):
def get(self, request):
now = datetime.datetime.now()
day = now.strftime("%B %d, %Y")
time = now.strftime("%I:%M %p")
return HttpResponse("It is now " + time + " on " + day + ".")
## mysite/urls.py
urlpatterns = [
url(r'^current-time-two/$', views.CurrentTimeTwo.as_view())
]
Django has a few classes that class-based views can inherit from, to make the process of creating views much easier. One of them is, of course, the View
class that we’ve already seen above, inherited by the CurrentTimeTwo
class. Others include:
TemplateView
: we saw this class in Part One of this tutorial when displaying a template in Django. Template views render a particular template, which is specified in the template_name
attribute. To learn more about this class, visit the documentation.RedirectView
: as the name implies, views that inherit from this class redirect the app to a particular URL, which is specified in the pattern_name
attribute. To learn more about this class, visit the documentation.DetailView
, which presents the details of a particular model object, and ListView
, which presents a list of model objects. In both cases, the model is specifed. To learn more about generic display views, visit the documentation.Forms allow a web app to accept user input from the front end. In this section, we’ll see how both Flask and Django handle forms.
Flask doesn’t have any support for forms out of the box. However, as is usually the case, there are extensions that come in to save the day. A fantastic option is Flask-WTF, which makes the process much easier. Flask-WTF integrates Flask with WTForms, a Python package that handles form input handling and validation.
To create a form, create a new file, forms.py
in your app directory. The following is a sign up form, which we can use to create a new user based on the User model we created earlier:
## app/forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.fields.html5 import EmailField
from wtforms.validators import DataRequired, Email
class SignUp(FlaskForm):
email = EmailField('Email', validators=[DataRequired(), Email()])
username = StringField('Username', validators=[DataRequired()])
first_name = StringField('First Name', validators=[DataRequired()])
last_name = StringField('Last Name', validators=[DataRequired()])
submit = SubmitField('Sign In')
Using WTForms allows us to easily handle field validation. Note that there are a variety of field types that you can specify, including EmailField
, StringField
, and SubmitField
, as well as validators. You can find more in the WTForms fields documentation. Once the form is done, you need to add a view to process the user input:
## app/views.py
from app import app, db
from .forms import SignUp
from .models import User
@app.route('/signup', methods=["GET", "POST"])
def signup():
form = SignUp()
if form.validate_on_submit():
user = User(email=form.email.data,
username=form.username.data,
first_name=form.first_name.data,
last_name=form.last_name.data)
db.session.add(user)
db.session.commit()
return 'Success!'
return render_template('signup.html', form=form)
The view above gets the data from the form and uses it to create a User object which is then added to the database. It then returns a simple success message.
The final step to incorporating the form is including it in a template so users can see it and fill in their data. As you can see in the view above, we specified the template to be signup.html
, so let’s go ahead and create it:
<!-- app/templates/signup.html -->
<html>
<head>
<title>Sign Up Page</title>
</head>
<body>
<h1>Fill out the form below to sign up</h1>
<br/>
<form action="{{ url_for('signup') }}" method="post">
<p>
{{ form.email.label }}<br>
{{ form.email }}
</p>
<p>
{{ form.username.label }}<br>
{{ form.username }}
</p>
<p>
{{ form.first_name.label }}<br>
{{ form.first_name }}
</p>
<p>
{{ form.last_name.label }}<br>
{{ form.last_name }}
</p>
<p>
{{ form.submit() }}
</p>
{{ form.csrf_token }}
</form>
</body>
</html>
And lastly, to make use of Flask-WTF’s Cross-Site Request Forgery (CSRF) protection feature, we need to include a secret key in the Flask configuration:
## app/__init__.py
app.config.update(dict(
SQLALCHEMY_DATABASE_URI='sqlite:////tmp/database.db',
SECRET_KEY="your secret key here" ## add this line
))
Now, run the Flask app and navigate to http://127.0.0.1:5000/signup
. You should see a simple, but perfectly functional form. If the specified database exists and has the User table and all its fields, then filling the form out should add a new user to the database.
Right out of the box, Django takes care of forms without the need to install any third-party packages. This includes processing submitted data from the front-end. Similar to Flask, working with forms in Django involves writing the form code, creating a view, and displaying the form in a template. To begin, create a forms.py
file in the new_app
directory.
Now, forms in Django can be manually configured, or based on a model. Manually configured forms inherit from Django’s form.Form
class, and are best used when the data being received is not tied to a model. A good example of this would be a contact form, as seen below:
## new_app/forms.py
from django import forms
class ContactForm(forms.Form):
email = forms.EmailField(label='Your Email Address', required=True)
subject = forms.CharField(label='Subject', max_length=100)
message = forms.CharField(label='Your Message', required=True)
Forms based on Django models inherit from the form.ModelForm
class. These are best used when the data received through the form will be inserted into a database table based on a Django model. Here’s an example based on our User model:
## new_app/forms.py
from .models import User
class SignUpForm(forms.ModelForm):
class Meta:
model = User
fields = ('email', 'username', 'first_name', 'last_name')
As we saw in Flask (courtesy of the WTForms package), Django too allows us to use a variety of field types and validators. More on this can be found in the field classes documentation and the validators documentation.
Now, to process our form, we need to create a view. Below I have created a function-based view:
## new_app/views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import SignUpForm
def sign_up(request):
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.email = request.POST.get('email')
user.username = request.POST.get('username')
user.first_name = request.POST.get('first_name')
user.last_name = request.POST.get('last_name')
user.save()
return HttpResponse("Success!")
context = {'form': form}
return render(request, 'signup.html', context)
The view takes the form data and uses it to populate a new user object, which is then saved. Again, a simple success message is returned. Now, let’s create a simple template to display the form:
<!-- new_app/templates/signup.html -->
<html>
<head>
<title>Sign Up Page</title>
</head>
<body>
<h1>Fill out the form below to sign up</h1>
<br/>
<form action="{% url 'signup' %}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
</body>
</html>
And lastly, let’s create the signup
URL to display the template. Add the following to the urlpatterns
list:
## mysite/urls.py
urlpatterns = [
url(r'^signup/$', views.sign_up, name='signup')
]
Now, run the Django app and navigate to http://127.0.0.1:8000/signup
. Again, you should see a simple, but functional sign up form. If you have properly set up your database by making migrations, then filling the form out should add a new user to the User table.
Tests are extremely important in software development. They help you ensure your code works as expected, and also catch any unexpected effects of changes to your code. In this section, I will give a brief overview of testing in both Flask and Django.
Flask makes use of Werkzeug, a WSGI (protocol that ensures web apps and web servers can communicate effectively) utility library for Python. It’s one of the Python packages that is installed when you first install Flask. During testing, Flask exposes the Werkzeug test client. This is useful because it allows you to send virtual requests to the Flask app. For this to work, it’s extremely important that the TESTING
configuration variable is set to True
.
Once that’s done, it’s up to you to select the tools and frameworks that you’ll use to write and run your tests. The choices are many, but personally, I have found much success with the Flask-Testing extension, used in conjuction with nose2 for running the tests. Note that you also need to create a test database. For an example of how I use Flask-Testing and nose2 for my Flask tests, please take a look at this article.
You can also visit the Flask testing documentation for more information and examples of testing in Flask.
Django has a test class, django.test.TestCase
, which inherits from Python’s unittest.TestCase
class. You should inherit from Django’s test class when creating your own test classes. Tests can be created in the pre-generated tests.py
file, like so:
## new_app/tests.py
from django.test import TestCase
from .models import User
## Create your tests here.
class MyTest(TestCase):
def setUp(self):
"""
This method is run before each test
"""
User.objects.create(
email="test@user.com",
username="testuser",
first_name="Test",
last_name="User")
def test_user_count(self):
"""
Test number of users in the database
"""
self.assertEqual(User.objects.count(), 1)
In Django, tests can be run using this command:
$ python manage.py test
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Destroying test database for alias 'default'...
Any file beginning with test
and ending with a .py
extension will be detected. The test command will run any test methods contained in the detected test files, as long as the methods also begin with test
. You can also specify which tests will be run by module, package, class, or even method. Note that Django automatically creates a blank test database, and then destroys it after running the tests. To learn more about tests in Django, visit the testing documentation.
Both Flask and Django have features that are interesting and worth knowing about. Some are also unique to their particular framework. Let’s take a look at some of them!
You can run an interactive Python shell based on your Flask app. To use it, simply specify the Flask application using the FLASK_APP
environment variable, and then run the flask shell
command:
$ export FLASK_APP = app.py
$ flask shell
Python 3.6.0 (default, Dec 24 2016, 00:01:50)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
App: app [debug]
Instance: /home/user/Projects/flask-app/instance
>>>
Using the Flask shell, you can explore your app via command line, and even add, edit and delete data in the database:
$ flask shell
>>> from app.models import User
>>> from app import db
>>> user = User(email="jane.doe@email.com", username="jane.doe", first_name="Jane", last_name="Doe")
>>> db.session.add(user)
>>> db.session.commit()
We’ve talked extensively (haha) about various Flask extensions in this tutorial, so by now you know just how useful they are in your Flask journey. Flask has a handy extensions registry, so feel free to peruse it to find new extensions you can make use of in your projects! And if you’re so inclined, there’s also some guidance on how to develop your very own Flask extension - check it out here!
Django ships with an admin interface that allows you to manage the data of your app. To access the admin site, start up your Django app and then navigate to http://127.0.0.1:8000/admin
. You’ll be presented with a login screen.
You’ll need to create a superuser to be able to login to the admin site, which you can do using the createsuperuser
command:
$ python manage.py createsuperuser
The command willl prompt you to select a username, email, and password which you can then use to login.
To add a model to the admin interface, open up the admin.py
file in your app folder and register it as follows:
from django.contrib import admin
from .models import User
## Register your models here.
admin.site.register(User)
Now we can view the User model in the admin interface and even add a new entry:
By now you have probably realised that Django comes with a lot more features out of the box than Flask, which is why we haven’t talked very much about third party Django packages in this tutorial. However, they do exist and can be quite useful if, like myself, you are averse to reinventing the wheel. Although there is no official Django packages registry, there is a site, https://djangopackages.org/, that lists various packages in various categories. Note that it was created and is run independently of the Django Software Foundation (the organization that develops and maintains Django).
And it’s a wrap! Now that you’ve read & you should know a lot more about both Flask and Django. And of course, you now have a clear and in-depth understanding of how the two frameworks compare, which means you are in a better position to choose one of them for your next web project. I’m looking forward to hearing your comments on this series, so feel free to share your thoughts in the comments below. Thanks for reading!
#python #flask #django #web-development #programming