Web Programming with Python

Web Programming Concepts

Web Programming Concepts

Overview of Client-Server Communications

In a typical client-server model, the client sends a request to the server over the network, and the server responds back.
The communication happens over the HTTP protocol, where web servers host web pages and listen to requests from web clients, such as browsers.
In practice, clients and servers communicate over a computer network on separate hardware, but both client and server may reside in the same system.

Understanding URLs and URIs

A Uniform Resource Locator (URL) is a specific type of Uniform Resource Identifier (URI) that is used to access resources on the internet. A URL specifies the means of accessing the resource by indicating its location on the computer network and a mechanism for retrieving it. A URI is a string of characters used to identify a name or a resource on the internet.
The main difference between URLs and URIs is that all URLs can be URIs, but not all URIs are URLs.
In web development, URLs are primarily used to locate web pages and resources like images, documents, and APIs. URIs are used more broadly to identify both resources on the web and names in various contexts, such as XML namespaces or RDF URIs.
Structure of a URL
A typical URL contains the following parts: protocol (e.g., HTTP, HTTPS), host (e.g., www.example.com), port (optional, e.g., :80), path (e.g., /index.html), and query string (optional, e.g., ?id=123).

                https://www.example.com:80/index.html?id=123
            

Introduction to REST APIs

REST (Representational State Transfer) API (Application Programming Interface) is a set of rules and constraints for building web services. It allows for interaction with RESTful web services, where everything is considered a resource and can be accessed using the standard HTTP methods (GET, POST, PUT, DELETE).
In RESTful APIs, there is a conventional mapping between the HTTP methods (GET, POST, PUT, DELETE) and the CRUD (Create, Read, Update, Delete) operations:
GET: Read operation. Used to retrieve data from the server. This method is idempotent, meaning it can be called many times without different outcomes.
POST: Create operation. Used to send data to the server to create a new resource. The data is included in the body of the request. It can also be used for other actions that don't fit into the CRUD model.
PUT: Update operation. Used to send data to the server to update an existing resource. The request body includes the updated data. PUT requests are idempotent; sending the same request multiple times will not result in different outcomes.
DELETE: Delete operation. Used to remove data from the server.

Building RESTful APIs with FastAPI

Building RESTful APIs with FastAPI

Overview

FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.8+ based on standard Python type hints.
It is high-performance, easy to learn, fast to code, ready for production
FastAPI is ideal for building modern APIs with automatic documentation and data validation.
FastAPI fully supports asynchronous programming using Python's async and await keywords. This makes it suitable for building high-performance, asynchronous APIs.
References:
FastAPI official site
FastAPI @Github

FastAPI Installation


            #install fastapi
            pip install fastapi

            # install ASGI server, for production such as Uvicorn:
            pip install "uvicorn[standard]"
        

Create Simple App and Run it


            from fastapi import FastAPI
            from fastapi.responses import HTMLResponse
            from typing import Union

            app = FastAPI()

            @app.get("/")
            async def root():
                html_content = "

Welcome to my site

" return HTMLResponse(content=html_content, status_code=200) @app.get("/items/{item_id}") def get_item(item_id: int, q: Union[str, None] = None): return {"item_id": item_id, "q": q}

Run it:


            uvicorn main:app --reload
        

Open your browser at http://127.0.0.1:8000 or http://127.0.0.1:8000/items/5?q=somequery

TodoApp with FastAPI

TodoApp with FastAPI

Create todoApp project

Create project and virtenv:

            mkdir simple_todoapp
            cd simple_todoapp

            # create virtenv:
            python -m venv ./.venv

            # activate virtualenv:
            source ./.venv/bin/activate
        

Dependency installations

Make sure you run next commands in main project folder and that your virtual env is activated

            # Install FastAPI and Uvicorn for async web serving
            pip install fastapi
            pip install "uvicorn[standard]"

            # Install packages for parsing multipart/form-data and Jinja2 for templates
            pip install python-multipart jinja2

            # Install SQLAlchemy for database interactions
            pip install sqlalchemy

            # Install sqlalchemy2-stubs for better type hinting with SQLAlchemy
            pip install sqlalchemy2-stubs

        

Create project structure


            |simple_todoapp
            └── src
                ├── app.py
                ├── db.py
                ├── models.py
                └── templates
                    └── base.html
        
On Bash, you can use next commands to create project structure

            mkdir -p simple_todoapp/src/templates
            touch simple_todoapp/src/{templates/base.html,app.py,db.py,models.py}
        

./src/app.py

This file is likely the entry point of your FastAPI application. It should contain the creation and configuration of your FastAPI app instance, route definitions, and possibly middleware configurations.
Code:

./src/models.py

In models.py, you define your SQLAlchemy models. These models represent the database structure:
Code:

                from sqlalchemy import Boolean, Column, Integer, String
                from db import Base

                class Todo(Base):
                    __tablename__ = "todos"

                    id = Column(Integer, primary_key=True)
                    title = Column(String(100))
                    complete = Column(Boolean, default=False)

                    def __init__(self, title: str):
                        self.title = title
            

./src/db.py

This file should handle the database connection setup using SQLAlchemy or another ORM/ODM of your choice.
You would define your SessionLocal class and engine here:

                from sqlalchemy import create_engine
                from sqlalchemy.orm import sessionmaker
                from sqlalchemy.ext.declarative import declarative_base

                # Database connection configuration
                DB_URL = "sqlite:///./todoapp.db"  # SQLite database file path

                # Create SQLAlchemy engine for database interaction
                engine = create_engine(DB_URL, connect_args={"check_same_thread": False})

                # Create a SessionLocal class for managing database sessions
                SessionLocal = sessionmaker(
                    autocommit=False,  # Disable autocommit to control transactions manually
                    autoflush=False,   # Disable autoflush to control flushing behavior
                    bind=engine        # Bind the session to the engine
                )

                # Base class for declarative model definitions
                Base = declarative_base()
            

./src/templates/base.html

The base.html file under the templates directory is a Jinja2 template (if you're following FastAPI's recommendations). It acts as a base layout for your HTML pages::

Start the server


                cd src/
                uvicorn app:app --reload
            
uvicorn: This is the command-line interface for Uvicorn, an ASGI server implementation, which is used to serve FastAPI applications. FastAPI is built on top of ASGI.
app:app: The first app refers to the Python file app.py (without the .py extension), which should be present in the current directory (src/ in this case). The second app is the variable name of the FastAPI application instance created inside app.py. So, app:app tells Uvicorn to look for a FastAPI application instance named app inside the app.py file.
--reload: This flag tells Uvicorn to automatically reload the server whenever it detects changes to the source code. This feature is very handy during development as it saves you from having to manually restart the server every time you make changes to your code. However, it is recommended to use this flag only in a development environment due to its performance implications.

FastAPI_TodoApp @GitHub

You can find the full project code at https://github.com/WWWCourses/FastAPI_TodoApp

Building Todo App with Django

Building Web Apps with Django

Overview

Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design.
Built by experienced developers, it takes care of much of the hassle of web development, so you can focus on writing your app without needing to reinvent the wheel.
It’s free and open source.
Reference: https://www.djangoproject.com/

Install dependencies for Todo App Project

Create and activate a virtual environment for your Django Todo App project:

                # Navigate to your projects directory
                cd path/to/your/projects

                # Create a virtual environment named 'venv'
                python -m venv venv

                # Activate the virtual environment
                # On Windows
                .\venv\Scripts\activate
                # On Unix or MacOS
                source venv/bin/activate
            
Now that the virtual environment is activated, you can install Djano packages using pip and they will be isolated from the global Python installation.

                pip install Django
            

Set up a Django Todo App Project


            # create project structure:
            django-admin startproject todo_project

            cd todo_project

            python manage.py startapp todo_app
        
django-admin startproject todo_project
This command initializes a new Django project by creating a directory named 'todo_project'. It sets up a basic Django project structure with a configuration directory containing settings.py, urls.py, asgi.py, and wsgi.py files. These files are essential for configuring the project, defining URL routes, and deploying the project to a server
python manage.py startapp todo_app
This command creates a new Django app named 'todo_app' within your project. It generates a directory with the same name (todo_app) containing the app structure, including models.py, views.py, and others, which are essential for defining your app's database models, routes, and views.

Configure the Django Project

Modify settings.py to include your app and database configuration:

            # todo_project/settings.py
            INSTALLED_APPS = [
                ...
                'todo_app',
            ]

            DATABASES = {
                'default': {
                    'ENGINE': 'django.db.backends.sqlite3',
                    'NAME': BASE_DIR / 'todoapp.db',
                }
            }
        

Define the Todo Model

Create your Todo model in models.py:
The Todo model defines a simple structure for todo items in a Django application. It consists of two fields: title, which is a character field to hold the name or title of the todo item, limited to 100 characters; and complete, which is a boolean field indicating whether the todo item has been completed, defaulting to False (indicating it's incomplete)

            # todo_app/models.py
            from django.db import models

            class Todo(models.Model):
                title = models.CharField(max_length=100)
                complete = models.BooleanField(default=False)

                def __str__(self):
                    return self.title
        

Run migrations

Run migrations command in your main todo_project folder to create your model tables.
These commands are used to apply database schema changes in a Django project

                python manage.py makemigrations
                python manage.py migrate
            

Create Forms and Views

Forms handle the logic of accepting and processing user data submitted through web forms. Django forms manage form rendering (to HTML), data validation, and error handling.
Views manage the logic of responding to user requests. They receive web requests, process data (retrieved or submitted), and return web responses, often by rendering templates filled with context data.

Create Forms

Create file todo_app/forms.py and define

                #todo_app/forms.py
                from django import forms
                from .models import Todo

                class TodoForm(forms.ModelForm):
                    class Meta:
                        model = Todo
                        fields = ['title']

            
This code snippet defines a Django form class named TodoForm, which will be linked directly to the Todo model
The Meta class inside it specifies which model the form is for and which fields should be included in the form. In this case, the form will include only the 'title' field from the Todo model.
This makes it easier to create forms for adding or editing instances of the Todo model, as Django will automatically generate the appropriate form fields based on the model fields specified.

Create Views

Create views in todo_app/views.py:

            # todo_app/views.py
            from django.shortcuts import render

            from django.shortcuts import render, redirect
            from .models import Todo
            from .forms import TodoForm

            def home(request):
                todos = Todo.objects.all()
                form = TodoForm()
                if request.method == 'POST':
                    form = TodoForm(request.POST)
                    if form.is_valid():
                        form.save()
                        return redirect('home')
                context = {'todos': todos, 'form': form}
                return render(request, 'todo_app/home.html', context)

            def update_todo(request, pk):
                todo = Todo.objects.get(id=pk)
                todo.complete = not todo.complete
                todo.save()
                return redirect('home')

            def delete_todo(request, pk):
                todo = Todo.objects.get(id=pk)
                todo.delete()
                return redirect('home')
        

URL Configuration

In Django, URLs define the mappings between URL paths and your views.
They tell Django which view to call for a given URL pattern, allowing your application to serve different content based on the URL accessed by the user.
This system is managed through a URLconf (URL configuration), typically set in the urls.py file of an app or project.

URL Configuration

Define URLs in todo_app/urls.py :

                # todo_app/urls.py
                from django.urls import path
                from . import views

                urlpatterns = [
                    path('', views.home, name='home'),
                    path('update/<int:pk>/', views.update_todo, name='update_todo'),
                    path('delete/<int:pk>/', views.delete_todo, name='delete_todo'),
                ]

            
and include them in the project's urls.py

                # todo_project/urls.py
                from django.contrib import admin
                from django.urls import path,include

                urlpatterns = [
                    path('admin/', admin.site.urls),
                    path('', include('todo_app.urls')),
                ]
            

Templates

In Django, templates are HTML files that allow Python-like expressions for dynamic data rendering.
They separate presentation from business logic by providing placeholders and tags to dynamically generate HTML content based on context data passed from views.
Templates are used to create dynamic web pages, where parts of the page change based on the context provided by Django views.

Create TodoApp Template

Put your HTML template in todo_app/templates/todo_app/home.html

Start the Server


                python manage.py runserver
            
This command starts a lightweight development web server on the local machine, making the Django project accessible through a web browser for testing and development purposes. It automatically reloads the code upon changes, facilitating rapid development.

These slides are based on

customised version of

Hakimel's reveal.js

framework