DEV Community

loading...
Cover image for FullStack Basics

FullStack Basics

tbhaxor profile image Gurkirat Singh ・8 min read

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

image

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

  1. Vagrant (For examples)
  2. Git
  3. Python3.x
  4. 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

Discussion (2)

pic
Editor guide
Collapse
gravesli profile image
gravesli

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~

Collapse
tbhaxor profile image
Gurkirat Singh Author

Hey @graves110, Nice tool