Course Introduction
You will learn how to make a data-driven web application using python. Web-application means, you will learn both the components; frontend and backend.
You will build a simple multi-user blogging application. That looks like
This course is taken from the udacity's FullStack Foundations course.
NOTE The image has been taken from one of the dev.to post
Requirements
- Vagrant (For examples)
- Git
- Python3.x
- pipenv
Vagrant is the software that configures the VM and lets you share files between your host computer and the VM's filesystem.
Download the vagrant box from here, and import it in the vagrant environment.
vagrant box add --name fullstack_foundations /path/to/file
Now boot up the vagrant box
vagrant init fullstack_foundations
vagrant up
vagrant ssh
Working with CRUD
Ok, so what you will do in a blogging application; create, read, update and delete posts. In short it is known as CRUD.
Whenever you are signing up, uploading media or writing a post, you are "creating" a data in the database to be retrieved later on. Seeing the data from other's or your profile, is considered as "reading" data. Similarily, when you are editing the existing entry of the data in the database, it is known as "updating". Removing the same information from the database is known as "deleting"
CRUD encapsulates all the opertions a user makes on the web.
A database is the persistant storage for all the CRUD operations. It means even if the server is turned off, you can perform same CRUD operations once you reboot up the server. SQL is the popular language for interacting with the database that encapsulates all the underlying operations done by DBMS (database management system; system that handles all the tasks for persistance storage).
SQL has 4 main queries that relates with CRUS
INSERT INTO `users` (`username`, `name`) VALUES ('user101', 'Ankit'); -- creating data
SELECT * FROM `users`; -- reading data
UPDATE `users` SET `name`='Anuj' WHERE `username`='user101'; -- updating data
DELETE FROM `users` WHERE `username`='user101'; -- deleting data
ORM (aka, Object Relational Mapper) is kind of framework that let's you encapsulate all the complex and scary SQL queries. It maps the SQL working to python class, so that developers focus more on business logic that to worry about these queries and potential vulnerabilities. In this course, I will be using popular ORM; SQLAlchemy
Installing SQLAchemy with pip
pip install --user sqlalchemy
To let class interact with sql via sqlalchemy, you need to inherit the sqlalchemy's declarative base class
from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta
Base: DeclarativeMeta = declarative_base()
Your class should have __tablename__
data variable, that tells sqlalchemy which table in the database these classes corresponds to. And then after defining the classes, you need to create the tables using create_all
method, which takes an engine
argument.
Base.metadata.create_all(engine)
The engine must be created before defining classes
from sqlalchemy.engine import create_engine, Engine
engine: Engine = create_engine("sqlite:///blogs.db")
The first part in the engine (sqlite:///
) is known as dialect, which is the type of database provider. It can be sqlite, mysql, mssql, postgres and many other sql providers.
So in our case, the users class would look like
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100))
uname = Column(String(10), nullable=False)
passwd = Column(String(20), nullable=False)
pass
Now you need one last thing, sessionmaker
to get a cursor of the database. Without this you won't be able to perform actions on database
from sqlalchemy.orm.session import Session, sessionmaker
DBSession = sessionmaker(engine)
session: Session = DBSession()
Creating a user with the session is just 3 lines of code when you are all set
user = User(name='Anuj', uaername='anuj', password='anuj101')
session.add(user)
session.commit()
So far you have learnt how to link to database and create a record. Now it's time to actually read the from the database. To list all the users the code is pretty similar
users = session.query(User).all()
This will give you list of all the users (List[User]
). To get the very first result, you need to call .first()
method
user = session.query(User).first()
This will give you a User
type object. So session.query(User)
will return a Query object. You can see all other methods from the link.
Updating an entry is very easy with sqlalchemy, you need to first get the object, update it's property and then add and commit to the session.
user = session.query(User).first()
user.name = 'My New Name'
session.add(user)
session.commit()
This will instantly update the matching entry in the database.
Deletion of an entry is similar to the above code, but here you have to use .delete()
instead of .add()
with session object.
user = session.query(User).first()
session.delete(user)
session.commit()
Making WebServer
In the webworld, you will often hear two words; client and server. Clients and servers are computer devices that communicate with each other over the internet.
A client is generally the one that initiates request to a server, and then server processes that request and send a response back to the client.
To ensure both clients and servers are speaking same language, protocols are the rules that enforces some format / ways to communicate with the server / client without confusion. HTTP is the protocol for web.
HTTP used TCP/IP protocol for better communication. HTTP meana Hyper Text Transfer Protocol, TCP means Transmission Control Protocol for connection oriented channel and IP means Internet Protocol for identifying and locating the end devices.
To make browser understand about the status of request, server returns a 3 digit number with the response known as status code. There are some common status codes.
200 - OK
201 - Created
401 - Unauthorized
404 - Not Found
502 - Bad Gateway
503 - Internal Server Error
To differentiate between requests, client sends HTTP verbs with it. There are 9 HTTP verbs but 2 of them are the most common; GET and POST. GET is used to request an existing resource, and if the resource is not found, server will return HTTP 404. POST is used to created or update the resource in the database.
In this section you will learn how to create a webserver with http.server
package. So a simple hello world server will look like
from http.server import BaseHTTPRequestHandler, HTTPServer
class RequestHandler(BaseHTTPRequestHandler):
# overriding do_GET method
def do_GET(self):
try:
# the request path
if self.path.endswith("/hello"):
self.send_response(200) # sending status code
self.send_header("Content-Type",
"text/html; charset=utf-8") # sending header
# ending header, this will let browsers know that after this there is response body
self.end_headers()
# sending http response body
body = "<html><body><h1>Hello World!</h1></body></html>"
self.wfile.write(body.encode())
else:
self.send_response(404)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.end_headers()
body = "Resource Not Found"
self.wfile.write(body.encode())
except IOError:
# error handling
self.send_error(503, "Internal Server Error")
pass
pass
pass
def run(host='0.0.0.0', port=5000, requestHandler=RequestHandler):
try:
# creating instance of HTTP server and binding to host and port with request handler
httpd = HTTPServer((host, port), requestHandler)
print("Press ^C to Exit")
# starting server
httpd.serve_forever()
except KeyboardInterrupt:
print("\rStopping")
httpd.shutdown() # stopping server of pressing CTRL C
if __name__ == "__main__":
run()
I am inheriting BaseHTTPRequestHandler
in the RequestHandler
class and extending the function do_GET
to handle all the GET requests. Handling POST request is done by do_POST
method and is demonstrated below
def do_POST(self):
file: BufferedReader = self.rfile # body
# reading header file
ctype = self.headers.get("content-type")
clen = self.headers.get("content-length")
# parsing to dict from string
if ctype == "application/json":
body = json.loads(file.read(int(clen)).decode())
else:
return self.send_error(503, "Accepts only JSON data")
# sending response
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
data = {"success": True, "body": body}
body = json.dumps(data)
self.wfile.write(body.encode())
in this the POST method is only accepting application/json
. Also you have seen that creating server handling requests are very time consuming and you as an developer have to be focused more on product than language. So in next section you will see a backend server framework which will handle all these for you under the hood
Using HTTP Server Framework
Frameworks are the developer's tools that simplifies the development process by taking care of all the trivial repetative codes and let you focus on the application.
In this topic, you will use flask framework to build the web application. Installing flask
pip install flask
Let's create same hello world application in flask
from flask import Flask
# creating flask app
app = Flask(__name__)
# handling routes
@app.route("/")
@app.route("/hello")
def hello_world():
# by default sends html response
return "Hello World"
if __name__ == "__main__":
# running server
app.run(debug=True, host='0.0.0.0', port=5000)
Did you see! In just 15 lines, we have covered up the hello world webserver. This is the power you will experience while working with frameworks.
Routing helps the server to uniquely identify the resource and clean slug type routings helps clients and users to remember the URL for the application. For instance, with this url /post/1
, everyone can easily figure out that client is expecting server to send details of the blog
So to create dynamic routing you need to add a placeholder like this
@app.route('/post/<int:id>')
def get_post_by_id(id):
pass
This will return, id
of type int
and later on you can use it in the function.
By default @app.route
listens for GET method, so to add POST method, you need to include methods=['POST']
. However this will listen only for POST method.
@app.route("/post", methods=['POST']
def save_post():
pass
As your application gets more complex, creating HTML response in the functions itself will be very complex and eventually at some time you will leave some bugs. So to overcome this issue, flask support templating of html pages, that let's to extend a template and then render it on the go. Flask uses jinja as the templating engine. You can include flask functions like url_for
in the template itself.
By default template folder used by flask is templates
in the current directory of the flask app entry point
To create more personalized user experience, you will be going to use sessions
. Sessions allows servers to store the information about the client and then fetch the details accordingly. To use sessions, flask needs to have a secret key used for encrypting the data.
app.secret_key = 'some random key'
Users must see some alerts for their interactions, for this you will be using flask's flash
.
Setting flash message
flash("Your message")
Getting flask messages in the template
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
Datafiles for the project are located in programs
folder. The virtual env in the vagrant is already sourced as soon as you will login the ssh. The main server is blog_server.py
which is depends on models.py
and database.py
. Other server files for course demonstration are also located in the same direcory
Iterative Development
Well, direct indulging into development seems to be a bit faster approach but eventually you will face too many bugs.
To overcome this issue, you should develop each feature one by one and addon the layer of the other when one is completed (developed and tested). By this your clients will have the full status of the project and your workflow will be well organised
To do so, you can simply create a rough model of the application by hand and then connect the dots of the user flow. By this you will end up with something called wireframe of the application, it is also called blueprint. Then later on when you will start the development, you will what to complete first and how.
Contact Me
Follow the links to reach me
- Email: tbhaxor@gmail.com
- Twitter: @tbhaxor
- Facebook: @tbhaxor
- GitHub: @tbhaxor
- LinkedIn: @gurkirat--singh
- Instagram: @_tbhaxor_
Top comments (2)
I think you are great! I built a state monitor using Flask!
Flask State Github:github.com/yoobool/flask-state .
Should i can get some improvement suggestions from anyone? Thanks~
Hey @graves110, Nice tool