Checking out under the hood
Tags: flask, python, beginners, programming
Published: 2023-10-20 13:50:27

If you’re reading this that would mean you most likely have some understanding of python and are beginning to look into web development. When a person wants to set up their web application they typically set up a client and server for a full-application. The client is the web browser that the user would interact with and depending on the interactions, requests would go through to the server, or backend, to manipulate the data. Here the server processes a request from the client, creates a response, and finally sends the response back to the client. This summarizes the goal for what a server should do. If you have come here, you want to understand a bit more in regards to flask. Flask is one of the go-to options to set up the backend of an application to handle the business you want. Here we’ll have a simple overview regarding flask and what you should prepare for.
	For flask you’re just focusing on the server so there won’t be any need for glamor or presentation, you’re just handling data so for those that are not too keen on their art skills back-end programming is for you. Let’s start off with the basic structure of models:

```
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import MetaData
from sqlalchemy.orm import validates
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy_serializer import SerializerMixin
import string, datetime


metadata = MetaData(
    naming_convention={
        "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
    }
)
db = SQLAlchemy(metadata=metadata)




class Patient(db.Model, SerializerMixin):
    __tablename__ = "patient_table"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String, nullable=False)


    # -list.object
    serialize_rules = ("-appointments.patient_object",)
    appointments = db.relationship(
        "Appointment", back_populates="patient_object", cascade="all, delete-orphan"
    )
    doctors = association_proxy("appointments", "doctor_object")




class Appointment(db.Model, SerializerMixin):
    __tablename__ = "appointment_table"


    # -object1.list -object2.list
    serialize_rules = ("-patient_object.appointments", "-doctor_object.appointments")


    id = db.Column(db.Integer, primary_key=True)
    day = db.Column(db.String)


    patient_id = db.Column(
        db.Integer, db.ForeignKey("patient_table.id"), nullable=False
    )
    doctor_id = db.Column(db.Integer, db.ForeignKey("doctor_table.id"), nullable=False)


    patient_object = db.relationship("Patient", back_populates="appointments")
    doctor_object = db.relationship("Doctor", back_populates="appointments")


    @validates("day")
    def validate_date(self, key, day):
        if day not in ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]:
            raise ValueError("appointments must be on a weekday")
        return day




class Doctor(db.Model, SerializerMixin):
    __tablename__ = "doctor_table"
    serialize_rules = ("-appointments.doctor_object",)
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String, nullable=False)
    specialty = db.Column(db.String, nullable=False)


    appointments = db.relationship(
        "Appointment", back_populates="doctor_object", cascade="all, delete-orphan"
    )
    patients = association_proxy("appointments", "patient_object")


    @validates("name")
    def validate_name(self, key, name):
        if not name.startswith("Dr."):
            raise ValueError("You can't hire a quack!")
        return name
```

This is the code that revolves around a scenario of a patient, appointments and doctors. Here we set up sql tables that possess relationships with one another for us to interact with. Depending on the relationships it could vary on how you want the interactions to play out. One interaction is that a patient can have many appointments, this means that the patient can have appointments with one or many doctors. The columns are set up for the primary id and the name of the patient. We carry over the primary id of the patient to keep track of what appointment they would have and what doctors they may interact with. The `db.relationship` we see creates the bridge from one model to the next. While the `association_proxy` sets up the “endpoints” between interactions removing some excess “middle” information if you called for this relationship instead of the `db.relationship`. So the bridge here heads to Appointments while the endpoint of the relationship is Doctors. Doctors here is similar to Patients as they are both the endpoints of the relationship except for an additional attribute called “specialty” however this is just an additional attribute so it will not affect the relationships between the models. And for the middle man of this scenario Appointment not only possesses the specific ids of patients and doctors but also the day which will typically be something done on the front end. The objects here `patient_object` and `doctor_object` will be the bridges that hold the data that interact with the endpoints of this relationship. You also see this code called `serialize_rules` which acts as a way to reduce unnecessary data from being sent for manipulation and to prevent recursion errors as the relationships would keep iterating themselves. The `validates(attribute)` will show an error message if some incorrect entry is attempting to pass. For the doctor it has a validate that checks the name parameter:
```
@validates("name")
    def validate_name(self, key, name):
        if not name.startswith("Dr."):
            raise ValueError("You can't hire a quack!")
        return name
```
Here it checks to see if the name starts with a string that possesses “Dr.” If the name entered does not start with that check it will return the error message.

That will be the intro to models and here the app.py is where the magic happens

```
from flask import Flask, make_response, jsonify, request
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
import datetime
from models import db, Doctor, Patient, Appointment

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app.db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.json.compact = False

migrate = Migrate(app, db)

db.init_app(app)


@app.get("/")
def index():
    return "doctor/patient"


@app.get("/doctors")
def get_doctors():
    doctors = Doctor.query.all()
    data = [doctor.to_dict(rules=("-appointments",)) for doctor in doctors]
    return make_response(jsonify(data), 200)


@app.get("/doctors/<int:id>")
def get_doctor_by_id(id):
    doctor = Doctor.query.filter(Doctor.id == id).first()
    if not doctor:
        make_response(jsonify({"error": "that is a quack"}), 404)
    doctor_dict = doctor.to_dict()
    return make_response(jsonify(doctor_dict), 200)


@app.get("/patients/<int:id>")
def get_patients(id):
    patient = db.session.get(Patient, id)
    doctors = [d.to_dict(rules=("-appointments",)) for d in patient.doctors]
    patient_dict = patient.to_dict(rules=("-appointments",))
    patient_dict["doctors"] = doctors
    return make_response(jsonify(patient_dict), 200)


@app.post("/doctors")
def post_doctor():
    data = request.get_json()

    try:
        doc = Doctor(name=data["name"], specialty=data["specialty"])
        db.session.add(doc)
        db.session.commit()
        return make_response(jsonify(doc.to_dict()), 201)
    except ValueError:
        return make_response(jsonify({"error": "that's a quack!"}), 405)


@app.patch("/patients/<int:id>")
def patch_patients(id):
    data = request.get_json()
    patient = Patient.query.filter(Patient.id == id).first()
    if not patient:
        make_response(jsonify({"error": "no such patient"}), 404)
    try:
        for key in data:
            setattr(patient, key, data[key])
        db.session.add(patient)
        db.session.commit()
        return make_response(jsonify(patient.to_dict()), 201)
    except:
        return make_response(jsonify({"error": "could not update patient"}), 405)


@app.post("/appointments")
def post_appointment():
    data = request.json
    try:
        appointment = Appointment(
            patient_id=data.get("patient_id"),
            doctor_id=data.get("doctor_id"),
            day=data.get("day"),
        )
        db.session.add(appointment)
        db.session.commit()
        return make_response(
            jsonify(appointment.to_dict(rules=("-patient_id", "-doctor_id"))), 201
        )
    except Exception as e:
        return make_response(jsonify({"error": str(e)}), 405)


if __name__ == "__main__":
    app.run(port=5555, debug=True)

```
Here the app processes the requests and manipulates the data according to the request. 

```
@app.get("/doctors")
def get_doctors():
    doctors = Doctor.query.all()
    data = [doctor.to_dict(rules=("-appointments",)) for doctor in doctors]
    return make_response(jsonify(data), 200)
```
In this ‘Get’ request the request calls for the data of all the doctors stored within the database and the data returned will be set based on the `serialize_rules` along with the `to_dict(rules = “-***”)` here the ‘Get’ also removes the appointments object so you basically just receive the doctors with their specialties and ids.

```
@app.post("/doctors")
def post_doctor():
    data = request.get_json()

    try:
        doc = Doctor(name=data["name"], specialty=data["specialty"])
        db.session.add(doc)
        db.session.commit()
        return make_response(jsonify(doc.to_dict()), 201)
    except ValueError:
        return make_response(jsonify({"error": "that's a quack!"}), 405)
```

Here a post request is made and the user enters the data for a doctor. However, you may receive an error if you forget about the validation rule set in the model. So if you’re post request forgets the rules no data will be entered into the database.

```
if __name__ == "__main__":
    app.run(port=5555, debug=True)
```
Finally this code should run the server for the user to interact with. Hopefully, this piece will give you an idea to prepare for when using flask.


Example code from [github](https://github.com/d-gaston/tours/tree/master/flask/cc/appointments/solution)