Why is Flask vs Django a common question? If you were to search for the “best programming language to learn” on Google, it would tell you that Python is one of the most frequently recommended languages for beginners. This should come as no surprise, since Python is one of the most user-friendly languages on the market today and forms the foundation of companies like Instagram, Pinterest, and Spotify.
Although when it comes to actually building an application in Python, there is no shortage of choices in the toolsets that you can use. However, in the web-driven world that we currently live in, APIs are king, and when it comes to Python, the two popular choices for building a scalable, performant REST API: Django and Flask.
Flask vs Django: Crafting a Fortune Teller API
The two most popular frameworks for Python, Django and Flask, take incredibly different approaches to web development. Django, the older of the two frameworks, is often called a “batteries included” framework, meaning that it contains just about everything you need to launch a full featured application in no time flat. Flask, on the other hand, is a highly extensible “micro-framework” that launches with a bare minimum set of features, but has a thriving plugin ecosystem which allows developers to only include the functionality that they need to succeed.
In order to demonstrate the differences between these two frameworks, let’s take a look at the process behind spinning up a basic “Fortune Cookie” REST API using each one. To keep things simple, this API will have only one endpoint, /fortune
, that will return a basic JSON response containing a random fortune.
That’s it. Nothing too fancy, but enough to get the gist of the complexities (or simplicities) of each framework.
If you want to follow along with the examples, make sure you have a recent version of Python installed along with Flask and Django. To get started quickly, you can either:
- Download and install the pre-built “Fortune Cookie API” runtime environment for Windows 10, macOS, CentOS 7, or…
- Download the pre-built Python WebDev Environment which contains all the popular frameworks, tools and utilities you’ll need to get your whole team up and running quickly no matter whether they use Mac, Windows or Linux.
From here on out, this article assumes that you have Flask and Django along with Python installed on your system and in your PATH
.
Flask vs Django: API Creation with Flask
Let’s go ahead and make our API endpoint. To do this, let’s first start with a very basic /fortune
endpoint that returns only one fortune. First, create a new file called app.py
in your favorite text editor, and enter the following code:
from flask import Flask, jsonify app = Flask(__name__) app.config["DEBUG"] = True @app.route('/fortune', methods=['GET']) def fortune(): return jsonify({ 'data': 'How many of you believe in psycho-kinesis? Raise my hand.', 'status': 'awesome' }), 200
To break the above block of code down, what we’re doing is defining a route which, in web application terms, is the part that comes after the domain name, such as the /fortune
in https://api.flower.codes/fortune
. The bit that comes after, the def fortune()
, is a function that processes requests to the /fortune route, which currently returns a single fortune.
Let’s make things a little more interesting and add a few more fortunes which we will select from at random to provide that unpredictable, fortuney goodness that we expect. To do this, we’ll mix in a little bit of Python magic, and add a few more bad fortune cookie proverbs:
from flask import Flask, jsonify import random app = Flask(__name__) app.config["DEBUG"] = True @app.route('/fortune', methods=['GET']) def fortune(): fortunes = [ 'A feather in the hand is better than a bird in the air. ', 'A golden egg of opportunity falls into your lap this month.', 'Bide your time, for success is near.', 'Curiosity kills boredom. Nothing can kill curiosity.', 'Disbelief destroys the magic.', 'Don't just spend time. Invest it.', 'Every wise man started out by asking many questions.', 'Fortune Not Found: Abort, Retry, Ignore?', 'Good to begin well, better to end well.', 'How many of you believe in psycho-kinesis? Raise my hand.', 'Imagination rules the world.', 'Keep your face to the sunshine and you will never see shadows.', 'Listen to everyone. Ideas come from everywhere.', 'Man is born to live and not prepared to live.', 'No one can walk backwards into the future.', 'One of the first things you should look for in a problem is its positive side.', 'Pick battles big enough to matter, small enough to win.', 'Remember the birthday but never the age.', 'Success is failure turned inside out.', 'The harder you work, the luckier you get.', 'Use your eloquence where it will do the most good.', 'What's hidden in an empty box?', 'Your reputation is your wealth.' ] return jsonify({ 'data': random.choice(fortunes), 'status': 'awesome' }), 200
Now all we have to do is start the API, which can be accomplished using the flask run
command (don’t forget to set the FLASK_APP
variable to the app.py
file we just created):
$ FLASK_APP=app.py flask run * Serving Flask app "app.py" * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: off * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
If we were to take the listed domain name and append our defined route, which is http://127.0.0.1:5000/fortune
, we should see a random fortune response in the JSON format we defined:
To test out the randomness, try reloading the page. You should get something different every time, and you will also be able to check “create a REST API” off of your bucket list
Flask vs Django: API Creation with Django
As we just saw, Flask is a very hands-off framework. It is minimally opinionated, and gives its users enough rope to hang themselves if they’re not careful. But what about Django? What makes it different? In a nutshell: features.
To understand what I mean by this, let’s first create a project. This will be different than the Flask example because Django projects are far more robust, which means that their scaffolding must be generated to get started instead of simply adding a few lines of code to a single .py
file:
$ django-admin startproject activestate_django_api_example
The above command creates a basic directory structure that defines our Django application. If you take a look at your file system, you should see a new folder based on the name of the project that you just created, with a handful of other files nested underneath.
+-- activestate_django_api_example +-- manage.py +-- activestate_django_api_example +-- __init__.py +-- settings.py +-- urls.py +-- wsgi.py
While each of these files can be used to modify the configuration and functionality of your application, we will be focusing primarily on the urls.py
file for the purposes of this demo. Before we can start building out our API, however, we must run the built-in database migrations first:
$ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying sessions.0001_initial... OK
These migrations initialize the database tables that are necessary for authentication, session management, and the admin interface (something that Flask does not ship with, for those of you who are following along at home).
To start the server as-is, all you have to do is run python manage.py runserver, which will start the server on port 8000
of your local machine:
$ python manage.py runserver Performing system checks... System check identified no issues (0 silenced). August 14, 2019 - 19:07:37 Django version 1.11.23, using settings 'activestate_django_api_example.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
As mentioned, Django ships with a built-in admin interface. So if we were to navigate to http://127.0.0.1:8000/admin
, we would see a page that looks similar to the following:
If, for example, we want to actually use the admin interface, we might want to create an admin user, like so:
$ python manage.py createsuperuser Username (leave blank to use 'zach'): zach Email address: zach@flower.codes Password: Password (again): Superuser created successfully.
Utilizing the admin interface is outside the scope of this article, but it is a great example of just how full-featured Django is in comparison to Flask (by design). The next thing that we need to do in order to create our Fortune API is to actually create the Fortune application in our Django project:
$ python manage.py startapp fortune
When run, the above command will create a new folder in our project directory called “fortune”, which will contain a handful of files that are similar to the ones that were already created:
+-- activestate_django_api_example +-- db.sqlite3 +-- manage.py +-- activestate_django_api_example +-- __init__.py +-- settings.py +-- urls.py +-- wsgi.py +-- fortune +-- __init__.py +-- admin.py +-- apps.py +-- models.py +-- tests.py +-- views.py +-- migrations +-- __init__.py
Django follows the Model-View-Controller standard, which means that in order to create our /fortune
API endpoint in Django we will have to add it to the views.py
file. Since we worked out the random fortune selection in our Flask example above, we will skip that step and go straight to the full functionality:
from django.shortcuts import render from django.http import JsonResponse import random # Create your views here. def fortune(request): fortunes = [ 'A feather in the hand is better than a bird in the air. ', 'A golden egg of opportunity falls into your lap this month.', 'Bide your time, for success is near.', 'Curiosity kills boredom. Nothing can kill curiosity.', 'Disbelief destroys the magic.', 'Don't just spend time. Invest it.', 'Every wise man started out by asking many questions.', 'Fortune Not Found: Abort, Retry, Ignore?', 'Good to begin well, better to end well.', 'How many of you believe in psycho-kinesis? Raise my hand.', 'Imagination rules the world.', 'Keep your face to the sunshine and you will never see shadows.', 'Listen to everyone. Ideas come from everywhere.', 'Man is born to live and not prepared to live.', 'No one can walk backwards into the future.', 'One of the first things you should look for in a problem is its positive side.', 'Pick battles big enough to matter, small enough to win.', 'Remember the birthday but never the age.', 'Success is failure turned inside out.', 'The harder you work, the luckier you get.', 'Use your eloquence where it will do the most good.', 'What's hidden in an empty box?', 'Your reputation is your wealth.' ] return JsonResponse({ 'data': random.choice(fortunes), 'status': 'awesome' })
As in our Flask example, our fortune()
method in our Django view will return a JSON response containing a random fortune and a basic status response. Next, we need to define our routes. While Flask handles this inline in one file, Django prefers to keep routes in a urls.py
file. Since this file is not included in the fortune
application scaffolding, we will first want to create it and add the following code:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^$', views.fortune, name='fortune'), ]
This code defines URL patterns that we want to match on and the relevant methods that get returned when those patterns are matched. In this example, the root route will execute our fortune view that was defined above. However, it’s important to understand that these routes haven’t been enabled yet. To do that, we will need to update the primary urls.py
file:
from django.conf.urls import url from django.contrib import admin from django.conf.urls import include urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^fortune/', include('fortune.urls')), ]
As you can see, our new urls.py
file has been included underneath the fortune/
namespace in our primary routes file. This means that while our fortune/urls.py
file indicates that only the root path should execute our fortune()
method, this root path is actually scoped underneath the fortune/ path
as dictated by our primary routes file. If we were to then visit our new http://127.0.0.1:8000/fortune
API endpoint, we should see a response that looks similar to our Flask example.
Flask vs Django – Which is Better?
Given our Fortune example, you’d be hard-pressed to find anyone who would argue that the Django solution is simpler than the Flask one. But these two frameworks are stunningly different by design. Django is an incredibly robust web application solution that isn’t suited to building REST APIs alone. You can do just about anything with Django (almost) out of the box, which makes it the perfect choice for developers who know exactly what they want to build, and don’t want to mess with building standard components from the ground up.
Flask, on the other hand, is proudly marketed as a micro-framework. It’s not intended to be a Django competitor, but rather a better alternative for developers who want fine-grained control over the design and development of their application. A great system for any new developer who wants to learn the ins-and-outs of web applications, Flask is also a powerful tool that offers just enough structure to move quickly, while at the same time offering the right amount of stability for experienced developers to build anything they want without having to make design or implementation compromises.
- To run the code in this blog post, you can download and install the pre-built “Fortune Cookie API” runtime environment for Windows 10, macOS, CentOS 7, or…
- Download the pre-built Python WebDev Environment which contains all the popular frameworks, tools and utilities you’ll need to get your whole team up and running quickly no matter whether they use Mac, Windows or Linux.
Related Blogs: