Flask is one of the best known python micro-frameworks.
It is easy to use, light weight, and fast.
Today we are going to use it to build what will eventually become a restaurant app.
Building a restaurant app is not as easy as it sounds.
Think of all the things you have to do before ordering food from an online menu;
- Look at different menu items
- Look at prices for those items
- Choose between carrWet or delivery
- Begin a checkout process
- Enter billing information
- Get estimate of when meal will be ready
That may be a long list but it’s ideally the type of process the user expectes and is probably accustomed to. These use cases are our requirements. Let’s translate them into design.
Ideally, the user should enjoy browsing different menu items;
Prices should be prominently displayed, the option to chose between carrWet or delivery should become available once the user has added a menu item onto their food tray.
The checkout process should be secure and the user should receive updates about their meal.
Essentially, we want our app to;
- Look good (and feel good)
- Be
responsive - Fairly fast
- Most importantly, be
secure
Design
Lets break down our app into two different design groups;
- Frontend
- Backend
Frontend design
Frontend design is going to be defined by a grid layout where various menu items will be displayed. It will also have; a search bar, and varoius search options for the user which will be prominently placed at the middle top of the page;
The grid layout will contain a wrapper for all the menu items available for order.
It is important that menu items display the total time it would take to prepare a food item.
(We shall see what comprises of a food menu item or food item somewhere down this page).
When the user begins a checkout process for their food items, count down timers for each of the food items will start and the total time remaining until the food items are ready will be displayed to the user.
So we will need;
- React
- Bootstrap or Foundation or Sass
- JavaScript
Backend design
We have to consider how the checkout process will playout and what technologies will help us scale properly especially for heavy loads.
There are a number of questions that we have to consider;
- How are we going to ensure or enforce security.
We want guest users to browse and checkout without having to register for an account.- Where do we store the user’s credentials when they sign up for an account?
- How menu items will be added and deleted from the app itself.
1. How we are going to ensure or enforce security.
There are a number of ways we can go about this;
- We can create a guest user account for the user until they checkout, create an account or exit
- We can bolster security by using 3rd party authentication services for that guest user
- Or we can force the user to sign up for the accounr. - Not good.
2. Where do we store the user’s credentials when they sign up for an account?
- Local app database
- Cloud database, like S3 or wasabi
- Use a 3rd part extension to handle autnentication.
Using 3rd part extensions relieves us of some problems but it doesn’t necessarily relieve us of responsibility. If that third-party extension or plugin fails, we will responsible for any damages incurred to the user.
We have to choose third-party extensions wisely. Do not sacrifice price for function and - importantly - security. If we feel confident enough to build it then we should do so.
Our final list of bakend tools is as follows;
- PostgresQL database + Amazon storage, S3 for images and user credentials
- Foundation + Sass + Javascript with some python for interface to backend
3. How menu items will be added and deleted from the app itself.
This is something that is going to be done many times over and it makes no sense to do it programatically. So a secure interface to the backend has to be created to allow the restaurant owners to add and remove images and to modify properties for each menu item as they see fit. Some of these properties include;
- Food item preparation time
- Food item image
- Food item name and description
I have described the properties of an object and that is what a food item is, an object.
Now we shall be mapping many of these objects to a database.
To do that we shall need an ORM. That is, an Object Relational Mapper.
Luckily for us there’s SQLALchemy.
Since we decided to store user credentials, we would have to sanitize them before storing them in the database, this often includes salting or encrypting them in some way.
The bcrypt package comes to mind while thinking of this but it’s too early to get into details.
Ultimately, I think an external package that does this sort of thing - and does it well - would be our best option for handlingauthentication.
However, most packages or extenstions that provide authentication serices are usually too expensive for a simple hobby app like ours so we will implement authentication.
It’s also fun to learn how to do it, correctly.
Migrations
Migrations become important when the schema of our database changes and we have to update every record in our database without destroying it. Usually when the schema changes, data gets lost. Migrations allow We to retain We data by making non-desctructive changes to the database.
So now we have the backend tools and a fairly decent mental picture of what we need to do;
- Secure checkout process
- Implement guest user that can checkout menu items
- Backend inteerface for addign and modifying existing items
Going back to the frontend
Let us revist that food menu item one more time;
- Image + image dimensions
- Title/name
- Descrition
- Date added
- Preperation time
- Price
Now our food menu item is complete! All that’s left is to define a model with these attributes for SQLAlchemy to map to our database.
SQLAlchemy abstracts away some of the footwork we would traditionally have to do via SQL queries to accomplish the same goals.
Things to keep in mind
- Responsiveness; The grid laWet has to accomodate mobile viewports in an elegant way
- Elegant design (frontend and backend). This usually goes without saying.
Show Me The Code!
We have talked a lot about what the web app will look like on the frontend and the backend.
Now we shall start setting up our app in a way that will allow us to build on what is at the foundation.
Before you begin
I’m going to be using Ubuntu. If We are not on a Linux-based system, I suggest using a Linux-based virtual machine or container otherwise We’re going to end up having issues in subsequent tutorials. For this tutorial it’s okay.
If We are on a Linux-based operating system, then Make sure We have the following installed;
Python(I am using 2.7)virtualenvvim/vscode(This is Optional)
Install python if We do not have it;
$ sudo apt-get install python
If We have Windows, then just go to the python website to download the latest python installer installer or the one corresponding to 2.7.16
We can also check and install pip similarly in case it is not installed.
For Windows users We can run the pip installer executable to install pip on our system.
We might also need to edit our system path to include both python and pip.
Getting started
Now that we have python and pip installed, we shall create a folder for the app;
$ cd ~/Documents
$ mkdir myapp
$ cd myapp
We can call it anything We want, I just ran out of ideas so I stuck with myapp.
$ virtualenv myenv
$ source myenv/bin/activate
The first command will install our virtual environment inside our application folder. Sometimes, this is important. Python and pip are some of the things that will be installed in the virtual environment.
That second command will activate our virtual environment. our console will look something like this;
$ (myenv)
Once our virtual environment is activated, we shall install the flask package by running this;
$ (myenv) pip install flask
This will allow us to create our flask application.
We can also specify the type of server We want our application to run on
$ export FLASK_ENV=development, and the name of the app;
$ export FLASK_APP=myapp
Setting up the app
We can just have everything inside one script by creating it;
$ (myenv) vi app.py
And adding the following code to it;
1. #!myenv/bin/python
2. from flask import Flask
3.
4. app = Flask(__name__)
5.
6. @app.route("/")
7. def index():
8. return "Welcome to my flask application."
9.
10. app.run("127.0.0.1")
We can then make the script accessible and executable by running the following command:
$ chmod a+x app.py
And run it; $ ./app.py
Navigate to 127.0.0.1:5000 or localhost:5000 in our browser.
We should expect to see a page displaying the words Welcome to my flask application congratulations.
I will now describe what each line of code in our app.py script does before breaking things up.
The lines are; 1, 2, 4, 6, 7, 10
Line 1. #!myenv/bin/python
The first line tells bash to execute our script using the instance of python installed in our virtual environment. We can use one virtual environment for multiple flask apps so it’s not necessary and is quite wasteful to have a virtual environment for every flask app We have (in my opinion). So if We want to build two or more flask applications using one virtual machine then it would have to reside one level above those flask applications.
For example;
/fenv
----myapp/
-------- app.py
----myapp2/
-------- app.py
----myapp3/
-------- app.py
We can then specify on the first line of each app.py script above:
#!myenv/bin/python
$ cd ../
then create a new virtual environment,
$ virtualenv fenv
then quickly go back to our flask app, activate it and install flask;
$ source ./myenv/bin/activate
$ (fenv) pip install flask
Don’t forget to change line 1 of our script so that it says;
#!myenv/bin/python
Line 2. from flask import Flask
Line 2 in our app.py imports a Flask object from the flask module. flask and Flask are two separate things. flask as I’ve said is a module.
Flask is an object that implements a WSGI application and acts as the central object. It accepts the name of the module or the package of the application. Once it is created it will act as a central registry for the view functions, the URL rules, template configuration and so on.
I wont get into what a WSGI application is, We can read more about that on Wikipedia if We want but I will say that Flask is based on Jinja2 and Werkzeug which is a comprehensive library of tools for WSGI applications.
Line 4. Raj = Flask(name)
The Flask object is what we use to create our app on line 4.
4. Raj = Flask(__name__)
The name that We see refers to the name of the module, in this case flask. We can also call our app anything, calling it app just makes things easier but We can do this;
1. #!myenv/bin/python
2. from flask import Flask
3.
4. Raj = Flask(__name__)
5.
6. @Raj.route("/")
7. def page_one():
8. return "Welcome to Raj's flask application."
9.
10. Raj.run("127.0.0.1")
Go ahead and try it out. $ (fenv) ./app.py
Line 6. @app.route(“/”) or @Raj.route(“/”)
Line 6 is what is known as a decorator. A decorator is essential a method that is able to “alter” the way a function behaves without having to change the source code of the function. If We already know what a decorator does then We can skip the code but here’s a brief example.
>>> def D(func): # a decorator for A function
>>> print("Function has been decorated!")
>>> return func
>>>
>>> @D
>>> def A():
>>> print("I am A.")
>>>
>>> "Function has been decorated!"
>>> D(A())
>>> "I am A"
>>> "Function has been decorated!"
So our app’s route method decorates our page_one() function in line 7. Raj.route takes in a URL rule, in this case “/”, and registers the view function – page_one(), for that URL rule – “/”.
In addition to the URL rule, we can also specify options such as;
Raj.route("/", methods=["GET"])
We didn’t have to specify that method because it’s the default method.
Line 7. def index(): or page_one():
This is just a view function which returns the string “Welcome to Raj’s flask application.” It’s registered to the URL rule we specify in our app’s route decorator. We can also pass objects and strings to this function for example
def page_one(global_string):
print global_string
# or an object
def page_one(some_object):
print some_object.get_message()
And so on. Of course We would have to define global_string and some_object
Line 10. app.run(“127.0.0.1”) or Raj.run(“127.0.0.1”)
Raj.run(“127.0.0.1”) essentially runs our flask application on a local development server at the localhost which is an alias the ip address 127.0.0.1
Now this method can take in the host, port, debug option, and other variables. We only specified the host in our case. By default, the app runs on port 5000 and debugging is disabled. So to run our app on a different port with debugging enabled we would have to specify it thus;
Raj.run("localhost", port=8000, debug=True)
The production environment is what runs by default. We would have to specify our option in a FLASK_ENV variable.
To specify a development environment for our application run;
$ export FLASK_ENV=development
Then we can either do;
$ flask run or
$ ./run.py
Full code listing.
#!myenv/bin/python
from flask import Flask
Raj = Flask(__name__)
@Raj.route("/", methods=["GET"])
def index():
return ("Welcome to Raj's flask application.")
Raj.run("localhost", port=8000, debug=True)
Breaking things up
All that is well and good but it’s gonna make it very cumbersome for us to develop our app because seperating business logic from app logic will become increasingly harder during development. So we will have to seperate our views from our models and controllers. We’ve got not models - yet but we will be following the M-V-C architectural pattern.
Right now we shouldn’t be worried because we have one view;
@Raj.route("/", methods=["GET"])
def index():
return ("Welcome to Raj's flask application.")
So lets break up in accordance with the M-V-C pattern.
Ideally we will want the structure of our app to be thus;
├── app
│ ├── __init__.py
│ ├── __init__.pyc
│ ├── static
│ │ └── style.css
│ ├── templates
│ │ └── index.html
│ ├── views.py
│ └── views.pyc
└── run.py
app/
This is the folder that will house everything about our app. It contains;
- An initialization script
__init__.py - A
static/directory for ourCSS - A
templates/directory for ourHTMLtemplates. views.py
__init__.py
In python the __init__ keyword is what represents a constructor and in this case represents our app if you think of it as a package.
In fact, the first step to making a python package is to have a script defined that way.
Here are the contents of our __init__.py file;
from flask import Flask
Raj = Flask(__name__)
from app import views
static/
The static/ directory houses all our css stylesheets.
Right now we’ve got no stylesheet but that will change in the second tutorial.
templates/
The templates/ directory houses our html files, but they are considered templates because we can pass python objects to them.
views.py
views.py file contains all our views, in this case;
@Raj.route("/", methods=["GET"])
@Raj.route("/index", methods=["GET"])
def index():
return ("Welcome to Raj's flask application.")
run.py
Finally, our run.py script is what will allow us to run our app.
#!myenv/bin/python
from app import Raj
Raj.run("localhost", port=3000, debug=True)
This compartmentalized form of our app allows us to work on our app efficiently.
Conclusion to part 1
We have barely started. There’s still a lot more work to do on the frontend and the backend
but at least we have created a foundation from which to actually begin working on the frontend
We will start with the frontend until we have met all the goals on that front :).
Some developers prefer starting with the backend and work their way towards the front but in my opinion it makes it easier to learn when we start from the frontend because it’s the least problematic fronts to deal with. It is really a matter of taste and perhaps project complexity, for example simple backend vs simple frontend.
It actually might make more sense for us to lay down some basic database connections and admin views so that we have items to add and delete. The model doesn’t have to be complicated.
This is actually the easy part of backend work, at least as far as the basics go.
It’s a good place to start in the next part of this series.
In addition to the database connections and an admin interface to that database we shall also create the basic layout of our restaurant;
cssstylesheets +sasspluginHTMLtemplates complete with sample images