Building MVP with Flask Day 1 – Dev setup
In this post, I will describe the process of starting a Flask project from scratch.
This post is a part of the article series about building an MVP with Flask
First things first.
I am not a software developer, so you might have a hard time following my thoughts or tutorial steps. My intention is not to make yet another cookie-cutter tutorial. There are plenty of them on Medium.
The main goal of this series is to document the whole process of making an MVP with emphasis on mistakes, that I make along the way.
Hardware & Software Requirements
For the record. The project is done on Macbook Pro 2018 with macOS Mojave 10.14.3.
That being said. All software tools, libraries or packages are tailored for Mac OS Unix environment. You might want to install Windows equivalents if you are a Windows user.
I used the following software in various stage of development:
- Homebrew
- iTerm2
- Nginx
- Gunicorn
- Python
- Visual Studio Code
- PyCharm
I won't cover them in this tutorial, so make sure you have them installed.
Create Project Folder
Let's start a project by creating a dedicated project folder.
I will call it Financia because it is the name of my domain. You can call it MyProject if you will.
mkdir financia
Now go to the newly created project folder.
cd financia
Setup Virtual Environment
Set a virtual python environment in the newly created project folder.
pipenv shell
This will create a special development environment just for our project. A sandbox, where we can install whatever python library we want without influencing system level installed python libraries.
Install Flask
Finally install Flask, a micro web framework for Python.
pipenv install flask
Installation failed due to some strange error.
Adding flask to Pipfile's [packages]...
Pipfile.lock not found, creating...
Locking [dev-packages] dependencies...
Locking [packages] dependencies...
lib/python3.7/site-packages/pipenv/utils.py", line 402, in resolve_deps
req_dir=req_dir
File "/usr/local/Cellar/pipenv/2018.7.1/libexec/lib/python3.7/site-packages/pipenv/utils.py", line 250, in actually_resolve_deps
req = Requirement.from_line(dep)
File "/usr/local/Cellar/pipenv/2018.7.1/libexec/lib/python3.7/site-packages/pipenv/vendor/requirementslib/models/requirements.py", line 704, in from_line
line, extras = _strip_extras(line)
TypeError: 'module' object is not callable
It looks like pip cannot install Flask.
After furious googling I found out, that is is caused by a bug in the newest pip version.
The solution is to revert to the older pip version.
pipenv run pip install pip==18.0
The older version of pip was reinstalled.
Collecting pip==18.0
Using cached https://files.pythonhosted.org/packages/5f/25/e52d3f31441505a5f3af41213346e5b6c221c9e086a166f3703d2ddaf940/pip-18.0-py2.py3-none-any.whl
Installing collected packages: pip
Found existing installation: pip 19.0.1
Uninstalling pip-19.0.1:
Successfully uninstalled pip-19.0.1
Successfully installed pip-18.0
Let's try to install Flask again.
pipenv install flask
Good. Flask was successfully installed.
Setup Python DEV IDE
It's time to decide which dev IDE or code editor to use. I can use either Visual Studio Code, which is a great text editor for any language, or PyCharm, a Python only dev IDE, that offers tight integration with Flask.
Integration with Flask is not turn-key though. Found a guide, that explain how to setup Flask with PyCharm. After 5 minutes of reading, I decided to skip this option.
I don't want to waste time in the beginning. Yes, I won't be able to use PyCharm debugger, but I can always set it up later.
Create Flask Boilerplate App
Anyway, let's create a new python file named app.py and insert this boilerplate code to create our first Flask app.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello World!"
if __name__ == '__main__':
app.run()
Thats it. We imported Flask library. Created an app called app with a method, that prints "Hello World!".
The method is wrapped with a route decorator, that tells the browser to execute the "hello" method if the user visits the homepage.
Setup custom localhost domain
It would be nice to assign custom localhost domain to our Flask app like "app.local" instead of "localhost" or "http://127.0.0.1".
But how?
I remember, that similarly to PHP and Wordpress, python web frameworks need an HTTP server and a proxy to serve python scripts to the browser.
In this case, the most frequent choice is to use Nginx and Gunicorn. Nginx is installed, but what about Gunicorn? Does it need to be installed individually in our virtual environment?
It appears so according to this guide.
Okay, install Gunicorn then.
pipenv install gunicorn
Now back to custom URLs. I need to add hosts to the Nginx configuration.
Go to folder /private/etc/hosts, where the hosts config file is located.
Open the file and add a custom domain.
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting. Do not change this entry.
##
127.0.0.1 financia.local
Type our custom domain into Chrome and try if it loads the app.
No, luck. Google is loaded instead.
Maybe restarting Nginx will help?
sudo brew services restart nginx
Or restart Gunicorn...
gunicorn financia.wsgi
Oops, this command doesn't work.
ModuleNotFoundError: No module named ‘financia'
Okay, what about running Flask directly from the command console? If it works, then we can continue.
python app.py
Yep, Flask works.
And browser shows...
We can stop Flask now.
ctrl+c
So Gunicorn works and the only thing left is Nginx configuration.
Go to folder /usr/local/etc/nginx/nginx.conf, open config file and add new configuration for our app.
server {
listen 80;
server_name financia.local;
access_log /financia/nginx/logs/access.log; # <- make sure to create the logs directory
error_log /financia/nginx/logs/error.log; # <- you will need this file for debugging
location / {
# checks for static file, if not found proxy to app
try_files $uri @proxy_to_app;
}
location @proxy_to_app {
proxy_pass http://financia.local:5000; # <- let nginx pass traffic to the gunicorn server
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /static {
root /fiancia/static/; # <- let nginx serves the static contents
}
}
Accordingly create new folder for Nginx logs /financia/nginx/logs/
And a root folder for static files /fiancia/static/
Restart nginx again.
sudo brew services restart nginx
Still not working. Maybe Gunicorn is missing some configuration?
Create new file gunicorn.conf.py in our project folder.
Add following config to the file.
bind = "financia.local:5000" # Don't use port 80 becaue nginx occupied it already.
errorlog = '/financia/gunicorn/logs/gunicorn-error.log' # Make sure you have the log folder create
accesslog = '/financia/gunicorn/logs/gunicorn-access.log'
loglevel = 'debug'
workers = 1 # the number of recommended workers is '2 * number of CPUs + 1'
Accordingly create new folder for Gunicorn logs /financia/gunicorn/logs/
Now try to enter url http://financia.local:5000 into Chrome.
Hmm, maybe http://financia.local will work instead of http://financia.local:5000.
Bad gateway error :(.
Time to look into Gunicorn documentation. Apparently, Gunicorn is not running at all.
Okay, found out, that the correct way to start Gunicorn with Flask is to execute command financia:app instead of financia:wsgi. WSGI is a Django framework thing, not Flasks.
Let's try a new command.
financia:app
The same error appears.
no module ModuleNotFoundError: No module named ‘financia'
Now I notice, that our Flask app doesn't contain any string with value "financia".
Could it be possible, that I should use the name of the flask App?
gunicorn app:app
Hurray! Now it works.
That would be all for today.
In the next session, I would like to add the Bootstrap 4 CSS framework, create a homepage using Flask templating system and design header for our app.