APIs, or Application Programming Interfaces, can be an extremely useful way of making your software accessible to users, potentially across the globe. Good programming language frameworks make it simple to create high-quality products quickly. They can even make the development process more enjoyable. FastAPI is a new Python web framework that checks all the boxes for being an excellent API development framework and is powerful and efficient. In addition, it integrates well with many Python GUI applications.
In this article, we will look at some interesting features of FastAPI, focusing on async methods and how to use them to create web APIs quickly. By the end, you’ll be able to start creating production-ready web APIs. We will then learn about creating your own Python GUI using DelphiFMX and PyScripter which I think is the best Python IDE. Finally, we will integrate the web API into the GUI application.
Table of Contents
What is the significance of asynchronous programming?
Asynchronous programming is a multi-tasking paradigm where a unit of work can run independently of the primary application thread. When the work is finished, it notifies the main thread of the worker thread’s success or failure.
This programming method has many key advantages; for instance, it is faster and more responsive than its synchronous counterpart. A program with asynchronous methods can become faster and present information to the user as it is processed, never allowing the project to become unresponsive and thus providing a better user experience.
This kind of programming even allows improved testing and error handling. If an operation in a different thread fails for whatever reason, you can handle the error or anomaly without restarting the entire project.
Furthermore, async ensures increased scalability since you can easily add new features or increase processing power to handle more requests by offloading operations to different threads.
Unfortunately, it also comes with some disadvantages, such as code complexity where code can become significantly more complex and difficult to debug, and thread overload which is where you overdo the amount of work the thread is doing which then impacts the synchronicity of your project so that becomes a hindrance rather than a help.
So, using asynchronous programming over synchronous programming isn’t a matter of what is better; it is a matter of when to use what.
You’ll generally want to use synchronous programming if your project focuses on a single task or if the tasks are highly time or response dependent and cannot run in parallel, such as web pages or video rendering. When you have a lot of tasks that don’t depend on each other or tasks that take a long time and can be run in the background, such as database manipulation, you’ll want to use asynchronous programming. Thus, any management system would be a good use case for async programming.
What is Python AsyncIO?
AsyncIO is a co-routine-based concurrency model in Python that provides elegant constructs for writing concurrent Python code without using threads.
This standard library package enables developers to create concurrent programs using async
/await
syntax. It enables developers to create unique functions known as co-routines for asynchronous programming by using a simple async
keyword and is thus regarded as the ideal fit for IO-bound and high-level structured network code in Python.
Is it possible to build an asynchronous Python service with FastAPI?
Yes, it is! Let us move on and start building an asynchronous employee management service in Python. But before we start with this application, there are some prerequisites that you need to install.
What are the prerequisites for creating an asynchronous service with Python?
First of all, you will need to install FastAPI and Uvicorn packages. You can easily install them using pip
by running:
1 |
pip install fastapi uvicorn |
FastAPI is a fairly new python web framework with built-in support for async API endpoints. The library will be used to construct a custom API for our employee management service. Meanwhile, Uvicorn is our chosen Asynchronous Server Gateway Interface (ASGI) server. This will help us run our app.
Next, you will need to install SQLAlchemy
and aiosqlite
using:
1 |
pip install SQLAlchemy aiosqlite |
SQLAlchemy
is the Python SQL toolkit and Object Relational Mapper (ORM) that gives application developers the full power and flexibility of SQL. We will use this to create our database that will store all employee information while we use aiosqlite
to interact with our database.
We can start building our service once we’ve installed our dependencies.
You can grab the code for this tutorial from our git repository at: github.com/Embarcadero/PythonBlogExamples/tree/main/Async_FastAPI_GUI_client
How to Use SQLAlchemy and AsyncIO to Create a Database?
Let’s begin by creating a database to store our Employee data.
First, we will configure our database using a simple Python script. To do this, create a file named employee.py
and first import the necessary packages:
1 2 3 4 5 |
# Importing packages from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy.orm import declarative_base, sessionmaker from sqlalchemy import Column, Integer, String |
Next, we will create a local instance of SQLLite
(a database named employees.db
):
1 |
DB_URL = "sqlite+aiosqlite:///./employees.db" |
We will use aiosqlite
to communicate with the database as it supports async
queries.
So, we create a database engine using the create_async_engine
function.
Then sessionmaker
creates a session with two unique flags, expire_on_comit
, and class_
. We set expire_on_comit
to False
, ensuring that our database entries and fields are always available even after a commit is made on our server session. Setting class_
to AsyncSession
lets our database know this is a new async
session:
1 2 3 4 5 6 7 8 |
# Creating a database instance engine = create_async_engine(DB_URL, future=True, echo=True) # Creating a session maker async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession) # Creating a base to configure DB models Base = declarative_base() |
Now, we are ready to configure our database.
Since we’re building an employee management service, our main database would contain some identifying attributes like id
and firstName
along with further details, such as joinYear
, which gives the year when the employee joined the organization. We can define our database by using the Base
we declared previously:
1 2 3 4 5 6 7 8 9 10 11 12 |
# Configuring our DB model with tablename "employees" class Employee(Base): __tablename__ = 'employees' # Initializing the columns and their data types id = Column(Integer, primary_key=True) firstName = Column(String, nullable=False) lastName = Column(String, nullable=False) gender = Column(String, nullable=False) role = Column(String, nullable=False) joinYear = Column(Integer, nullable=False) |
Note that using the __tablename__
attribute above, we initialize the name of the database. We then assign each column a respective Column
object, assign their data types, and declare whether they can be null using nullable
, and specify whether they can be used as a primary_key
for the database.
Here is what our final employee.py
file looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# Importing packages from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy.orm import declarative_base, sessionmaker from sqlalchemy import Column, Integer, String # Configuring DB_URL = "sqlite+aiosqlite:///./employees.db" # Creating a database instance engine = create_async_engine(DB_URL, future=True, echo=True) # Creating a session maker async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession) # Creating a base to configure DB models Base = declarative_base() # Configuring our DB model with tablename "employees" class Employee(Base): __tablename__ = 'employees' # Initializing the columns and their data types id = Column(Integer, primary_key=True) firstName = Column(String, nullable=False) lastName = Column(String, nullable=False) gender = Column(String, nullable=False) role = Column(String, nullable=False) joinYear = Column(Integer, nullable=False) |
To create a new entity, we will use a Data Access Layer (DAL), which will be responsible for all the SQL functions for our database. We do this by creating a new file, employee_dal.py
, and adding some package imports:
1 2 3 4 5 6 |
from typing import List, Optional from sqlalchemy import update, delete from sqlalchemy.future import select from sqlalchemy.orm import Session from employee import Employee |
Next, we define a new class named EmployeeDAL
that will have four methods:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
class EmployeeDAL(): def __init__(self, db_session: Session): self.db_session = db_session async def create_employee(self, firstName: str, lastName: str, gender: str, role: str, joinYear: int): # Creating an Employee object new_employee = Employee(firstName=firstName,lastName=lastName, gender=gender, role=role, joinYear=joinYear) # Saving employee to the database self.db_session.add(new_employee) await self.db_session.flush() async def get_employees(self) -> List[Employee]: # Retrieving all employees from database and returning them q = await self.db_session.execute(select(Employee).order_by(Employee.id)) return q.scalars().all() async def update_employee(self, employee_id: int, firstName: Optional[str], lastName: Optional[str], gender: Optional[str], role: Optional[str], joinYear: Optional[int]): # Retrieve employee from database q = update(Employee).where(Employee.id == employee_id) # Change the employee details if firstName: q = q.values(firstName=firstName) if lastName: q = q.values(lastName=lastName) if gender: q = q.values(gender=gender) if role: q = q.values(role=role) if joinYear: q = q.values(joinYear=joinYear) # Update employee entity in the database q.execution_options(synchronize_session="fetch") await self.db_session.execute(q) async def delete_employee(self, employee_id: int): # Deleting an employee from database and returning them await self.db_session.execute(delete(Employee).where(Employee.id == employee_id)) |
The create_employee()
function receives details regarding an employee and creates a new Employee
object. It then uses the object to save a new record to the database. A get_employees()
function returns all the employees in the database in ascending order based on the employee id. The update_employee()
function receives an employee id and optional new fields for the employee to update in the database. Lastly, the delete_employee()
function receives an employee id
and deletes the mentioned employee from the database.
It is important to note that in the update_employee()
function, we add an execution parameter called synchronize_session. This method tells the session to “refresh” the updated entries using the fetch
method, ensuring that all data in the memory will be up-to-date with the newly updated employee values.
Now that our database is ready, we can create an endpoint for our management service.
How to create an API endpoint with FastAPI?
Let’s start by creating a new file named app.py
and importing symbols from the necessary packages:
1 2 3 4 5 6 7 |
# Importing necessary packages from typing import List, Optional import uvicorn from fastapi import FastAPI from employee import Employee, engine, Base, async_session from employee_dal import EmployeeDAL |
We then create an instance of FastAPI that will handle all the endpoints of our application:
1 2 3 |
# Creating an app instance app = FastAPI() |
Next, we create a startup function using the @app.on_event("startup")
decorator, that will run when we start our application:
1 2 3 4 5 6 7 8 |
# Initializing the database on startup @app.on_event("startup") async def startup(): # create db tables async with engine.begin() as conn: await conn.run_sync(Base.metadata.drop_all) await conn.run_sync(Base.metadata.create_all) |
The startup()
function initializes our FastAPI and creates an instance of our database. This is done by either reading an already existing instance of employees.db
or creating a new instance of our database.
All endpoints we make will asynchronously create a DB session and an EmployeeDAL instance with the session and then call their respective DAL functions. The main endpoint we are making is called employees
and will have three methods. So, let us look at how we can create different methods for our employees
endpoint.
What Will the GET Method Be?
The GET
method for our employee management service will request the database to return all the employees:
1 2 3 4 5 6 7 8 9 10 |
# Creating a GET endpoint @app.get("/employees") async def get_all_employees() -> List[Employee]: # Creating an async session async with async_session() as session: async with session.begin(): # Get and return all employees from database employee_dal = EmployeeDAL(session) return await employee_dal.get_employees() |
The GET
method creates an asynchronous session using async_session()
. It then creates an instance of EmployeeDAL
and later uses the get_employees()
DAL function to get all of the employees in our database.
What Will the POST Method Be?
The POST
method will serve to add an employee to our database. To do this, we will utilize the create_employee()
DAL function that we created previously. This method’s response will be the details of the successfully added employee.
1 2 3 4 5 6 7 8 9 10 11 12 |
# Creating a POST endpoint @app.post("/employees") async def create_employee(firstName: str, lastName: str, gender: str, role: str, joinYear: int): # Creating an async session async with async_session() as session: async with session.begin(): # Creating an EmployeeDAL instance (i.e., our employee) employee_dal = EmployeeDAL(session) # Adding said employee to the database await employee_dal.create_employee(firstName, lastName, gender, role, joinYear) return {"firstName": firstName, "lastName": lastName, "gender": gender, "role": role, "joinYear": joinYear} |
What Will the DELETE Method Be?
The DELETE
method will help us delete employees from our database. We will do this by the delete_employee()
DAL function. The method will return the id
of the deleted employee.
1 2 3 4 5 6 7 8 9 10 |
@app.delete("/employees/{employee_id}") async def delete_employee(employee_id: int): # Creating an sync session async with async_session() as session: async with session.begin(): employee_dal = EmployeeDAL(session) # Update employee with new updated details await employee_dal.delete_employee(employee_id) return {"id": employee_id} |
What Will the PUT Method Be?
Finally, we create a PUT
method that will allow us to update an existing employee in our database. This is done by calling the update_employee()
DAL function in an EmployeeDAL
instance. This method will simply return the id
of the employee whose details have just been updated successfully.
1 2 3 4 5 6 7 8 9 10 11 12 |
# Creating a PUT endpoint @app.put("/employees/{employee_id}") async def update_employee(employee_id: int, firstName: Optional[str] = None, lastName: Optional[str] = None, gender: Optional[str] = None, role: Optional[str] = None, joinYear: Optional[int] = None): # Creating an sync session async with async_session() as session: async with session.begin(): employee_dal = EmployeeDAL(session) # Update employee with new updated details await employee_dal.update_employee(employee_id, firstName, lastName, gender, role, joinYear) return {"id": employee_id} |
How to Run the Server?
Finally, to run our server, we will run our app
using uvicorn
:
1 2 3 4 |
# Starting a FastAPI application if __name__ == '__main__': uvicorn.run("app:app", port=8000, host='127.0.0.1') |
The code above starts our FastAPI application using the uvicorn
ASGI server on port 8000
with our custom endpoint.
Here is what our final server file (app.py)
looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# Importing necessary packages from typing import List, Optional import uvicorn from fastapi import FastAPI from employee import Employee, engine, Base, async_session from employee_dal import EmployeeDAL # Creating an app instance app = FastAPI() # Initializing the database on startup @app.on_event("startup") async def startup(): # create db tables async with engine.begin() as conn: await conn.run_sync(Base.metadata.drop_all) await conn.run_sync(Base.metadata.create_all) # Initial get endpoint. @app.get("/") async def home(): return "Employee Management! Visit Swagger Docs to test it out." # Creating a POST endpoint @app.post("/employees") async def create_employee(firstName: str, lastName: str, gender: str, role: str, joinYear: int): # Creating an async session async with async_session() as session: async with session.begin(): # Creating an EmployeeDAL instance (i.e., our employee) employee_dal = EmployeeDAL(session) # Adding said employee to the database await employee_dal.create_employee(firstName, lastName, gender, role, joinYear) return {"firstName": firstName, "lastName": lastName, "gender": gender, "role": role, "joinYear": joinYear} # Creating a GET endpoint @app.get("/employees") async def get_all_employees() -> List[Employee]: # Creating an async session async with async_session() as session: async with session.begin(): # Get and return all employees from database employee_dal = EmployeeDAL(session) return await employee_dal.get_employees() @app.delete("/employees/{employee_id}") async def delete_employee(employee_id: int): # Creating an sync session async with async_session() as session: async with session.begin(): employee_dal = EmployeeDAL(session) # Update employee with new updated details await employee_dal.delete_employee(employee_id) return {"id": employee_id} # Creating a PUT endpoint @app.put("/employees/{employee_id}") async def update_employee(employee_id: int, firstName: Optional[str] = None, lastName: Optional[str] = None, gender: Optional[str] = None, role: Optional[str] = None, joinYear: Optional[int] = None): # Creating an sync session async with async_session() as session: async with session.begin(): employee_dal = EmployeeDAL(session) # Update employee with new updated details await employee_dal.update_employee(employee_id, firstName, lastName, gender, role, joinYear) return {"id": employee_id} # Starting a FastAPI application if __name__ == '__main__': uvicorn.run("app:app", port=8000, host='127.0.0.1') |
So, to run the server, simply execute your app.py
python file. This will start an instance of your FastAPI application, ready to receive any API calls.
How to utilize a Python service as a client?
There are several ways to make API calls; we will explain them briefly below.
What is Swagger?
When you visit http://127.0.0.1:8000/docs in your browser, an automatic API documentation with all of the endpoints will appear. This is the Swagger UI.
The Swagger UI is automatic, interactive API documentation created while creating your custom FastAPI endpoints. Here you will have options to make calls to your API’s various endpoint methods.
For example, you can make a POST
call to add an employee to your database:
Once you execute this, you will get a similar response as below:
As you can see, the response object returns a JSON that provides all the attributes of the newly added employee in your database.
What are query parameters?
As shown in the above screenshots of Swagger Docs, all the attributes we provide to swagger while making API calls are query parameters.
Other function parameters declared but not included in the path parameters are automatically treated as “query” parameters.
In addition to the Swagger UI, we can use traditional query parameters to send requests to our service. The query is the group of key-value pairs that come after the ?
in a URL and are separated by &
characters.
For example, a URL representing a POST
call to add an employee:
1 |
http://127.0.0.1:8000/employees/?firstName=Tom&lName=Sheldon&gender=M&role=Manager&joinYear=2015 |
How to use the requests Python package to create a client for an API?
Finally, you can also use the HTTP
protocol to send client requests to the server, retrieve specific data, and also information on whether the transaction was successful.
Python has many packages that allow you to send and receive data from your client and server, but for this example, we will use the Python’s requests package to send and examine exchanges between the client and server.
Let’s install the requests package and confirm it’s installation:
1 |
pip install requests |
To create a client endpoint using HTTP
protocols, first, create a new file named simple_client.py
import the requests
package in your code:
1 |
import requests |
Next, configure your base URL according to your server’s uvicorn running instance:
1 |
BASE = "http://127.0.0.1:8000/" |
You can now send a GET
request using get()
method of requests
package. This will fetch all the retrieved data using our API call:
1 2 3 4 5 |
# GET response = requests.get(BASE + "employees") response = response.json() print(response) |
Similarly, you can send a POST
request using the requests.post
method, but this time defining the query parameters:
1 2 3 4 5 6 7 8 9 10 11 |
# POST endpointURL = BASE + "employees" params = {"firstName": "Fifa", "lastName": "Badar", "gender": "F", "role": "Teacher", "joinYear":"2022" } response = requests.post(endpointURL, params=params) response = response.json() print(response) |
Though we can communicate with our service manually, this is quite time-consuming. Thus, the best way to exchange information between the client and the server is through a GUI. We will discuss this later in the article with one of the best options for GUI in Python.
Why should you use PyScripter to create APIs?
An integrated development environment (IDE) can be the difference between a good and bad programming experience. Moreover, a good IDE simplifies code development management, saving time and frustration, and making coding an overall good experience. This is where PyScripter outperforms its competitors.
PyScripter is currently the best IDE, and it started as a simple IDE to supplement the excellent Python for Delphi (P4D) components by providing a reliable scripting solution for Delphi applications. It has a more modern user interface and is faster than some other IDEs because it is written in a compiled language. It also has several features that make it an excellent Python development environment.
This amazing IDE includes handy features such as brace highlighting, code folding, code completion, and syntax checking as you type. It includes a Python interpreter that allows users to run Python programs without saving them. Finally, this IDE accepts files dropped from Explorer or other browsers applications, which could save you time making it an ideal IDE for developing and testing APIs.
How can we build a Python GUI for this API?
Making a simple graphical user interface (GUI) can be difficult, but it doesn’t have to be. Many Python libraries allow you to create GUIs; however, none are comparable to the DelphiFMX package, which allows you to create attractive user interfaces that you and your users will appreciate.
What is DelphiFMX?
DelphiFMX is a Python module that is natively compiled and powered by the Python4Delphi library. This cross-platform framework is freely redistributable and provides Python developers access to the FireMonkey GUI framework. It is versatile and provides a robust structure for Windows, Linux, macOS, and Android.
How can we use the DelphiFMX library to create a stunning Python GUI?
The DelhpFMX package for Python allows us to create powerful and stunning GUIs with minimal code. But before creating a GUI for our service, we first have to install DelphiFMX. So, open up the terminal and run the following command:
1 |
pip install delphifmx |
Once installed, create a new Python file, eg. FMX_GUI_client.py
, in your employee management service code directory.
Now, we can start building our service. First, import the relevant packages and initialize the BASE
as the server URL:
1 2 3 4 |
import requests from delphifmx import * BASE = "http://127.0.0.1:8000/" # Base URL where the web service is being hosted locally |
Next, we define an EmployeeManager
class that will define our main form:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
import requests from delphifmx import * BASE = "http://127.0.0.1:8000/" # Base URL where the web service is being hosted locally class EmployeeManager(Form): def __init__(self, owner): self.SetProps(Caption = "Employee Management", Width = 600, Height = 420) # Adding labels for each textbox self.fName = Label(self) self.fName.SetProps(Parent = self, Text = "First Name: ", Position = Position(PointF(20, 20))) self.lName = Label(self) self.lName.SetProps(Parent = self, Text = "Last Name: ", Position = Position(PointF(20, 50))) self.gender = Label(self) self.gender.SetProps(Parent = self, Text = "Gender: ", Position = Position(PointF(20, 80))) self.role = Label(self) self.role.SetProps(Parent = self, Text = "Role: ", Position = Position(PointF(20, 110))) self.year = Label(self) self.year.SetProps(Parent = self, Text = "Year Joined: ", Position = Position(PointF(20, 140))) self.id = Label(self) self.id.SetProps(Parent = self, Text = "Employee ID: ", Position = Position(PointF(400, 60))) # Adding textboxes self.editFName = Edit(self) self.editFName.SetProps(Parent = self, Position = Position(PointF(100, 20)), Height = 20) self.editLName = Edit(self) self.editLName.SetProps(Parent = self, Position = Position(PointF(100, 50)), Height = 20) self.editGender = Edit(self) self.editGender.SetProps(Parent = self, Position = Position(PointF(100, 80)), Height = 20) self.editRole = Edit(self) self.editRole.SetProps(Parent = self, Position = Position(PointF(100, 110)), Height = 20) self.editYear = Edit(self) self.editYear.SetProps(Parent = self, Position = Position(PointF(100, 140)), Height = 20) self.editId = Edit(self) self.editId.SetProps(Parent = self, Position = Position(PointF(400, 80)), Height = 20) # Adding buttons to call the 3 methods of our API endpoint self.addEmployee = Button(self) self.addEmployee.SetProps(Parent = self,Text = "Add Employee", Position = Position(PointF(250, 110)), Width = 100, OnClick = self.__button_click_add) self.view = Button(self) self.view.SetProps(Parent = self, Text = "View Employees", Position = Position(PointF(250, 140)), Width = 100, OnClick = self.__button_click_view) self.update = Button(self) self.update.SetProps(Parent = self, Text = "Update Employee", Position = Position(PointF(400, 110)), Width = 100, OnClick = self.__button_click_update) self.delete = Button(self) self.delete.SetProps(Parent = self, Text = "Delete Employee", Position = Position(PointF(400, 140)), Width = 100, OnClick = self.__button_click_delete) # List box to display all API call responses or any other messages self.list = ListBox(self) self.list.SetProps(Parent = self, Position = Position(PointF(20, 170)), Width = 550, Height = 200) |
Here we are creating six labels: fname, lname, gender, role,
year
, and id
using DelphiFMX’s Label
class. We then create textboxes for each defined label using the Edit
class. These textboxes will take input from the user to add some values to our application. Finally, we define three buttons addEmployee
, view
, and update
, which will allow us to add employees to our database, view all the employees, or update the employee details in our database respectively. You can optionally use TColor to define the colors for your GUI.
Now, we define private class methods that add functionality to our GUI and its buttons:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
def __button_click_add(self, sender): # Called when addEmployee button clicked data = {"firstName": self.editFName.text, "lastName": self.editLName.text, "gender": self.editGender.text, "role": self.editRole.text, "joinYear": int(self.editYear.text)} endpointURL = BASE + "employees" response = requests.post(endpointURL, params=data) self.list.items.add("Your Entry was successfully added!") self.list.items.add(response.json()) self.editFName.text = self.editLName.text = self.editGender.text = self.editRole.text = self.editYear.text = "" def __button_click_update(self, sender): # Called when update button clicked # If conditions to support the optional feature of your update call if self.editFName.text=="": fname = None else: fname = self.editFName.text if self.editLName.text=="": lname = None else: lname = self.editLName.text if self.editGender.text=="": gender = None else: gender = self.editGender.text if self.editRole.text=="": role = None else: role = self.editRole.text if self.editYear.text=="": year = None else: year = int(self.editYear.text) endpointURL = BASE+"employees/" data = {"firstName": fname, "lastName": lname, "gender": gender, "role": role, "joinYear": year} response = requests.put(endpointURL+str(self.editId.text), params=data) response = response.json() self.list.items.add("Your Entry was successfully updated!") self.list.items.add(response) def __button_click_view(self, sender): # Called when view button clicked response = requests.get(BASE + "employees") response = response.json() if response != []: for employee in response: self.list.items.add(employee) else: self.list.items.add("Your Employee Database is empty!") def __button_click_delete(self, sender): endpointURL = BASE + "employees/" response = requests.delete(endpointURL+str(self.editId.text)) response = response.json() self.list.items.add("Your Entry was successfully deleted!") self.list.items.add(response) |
Lastly, we will define the main method that runs our GUI:
1 2 3 4 5 6 7 8 |
def main(): Application.Initialize() Application.Title = "Employee Management Using Delphi FMX" Application.MainForm = EmployeeManager(Application) Application.MainForm.Show() Application.Run() Application.MainForm.Destroy() |
We can finally call our main function to run our GUI application. Optionally, you can add the following lines of code to run the GUI whenever the file is run:
1 2 3 |
if __name__ == '__main__': main() |
What does our Python GUI look like?
Running the code will give the following GUI:
If your server is running, then your GUI must be completely functional, and you can start making calls to your endpoints.
Clicking the View Employees button will display all the employees in your database:
We can see that the employee we added earlier using the swagger UI is in our database.
You can fill in all the employee details and add an employee to your database using the Add Employee button.
Below, you can see that an employee was successfully added to the database:
Similarly, you can even update an employee’s details using the Update Employee button. Let’s consider that Jane has changed her last name. So we will type her id
in the Employee ID text box and then update the field that we need to.
Note that, to make updates using the PUT call, giving any parameter other than Employee ID is optional.
Now let’s view the list of employees again using View Employees to see if the changes have been reflected:
We can see that the changes have been made! Now, let’s even test our async API’s delete functionality by deleting the employee using the Delete Employee button with id
number 1.
We can see the success message and API request’s appropriate response, but let’s view employees one last time to ensure the deletion:
Are you ready to create your own APIs using FastAPI and Python GUI?
Great job on making it this far! We have successfully built an employee management service, using FastAPI and SQLAlchemy, that maintains an organization’s employee data. New employees can be added here, while existing employee details can be viewed or updated. Since this service is an example of database management, it was the perfect use case for asynchronous programming.
Hopefully, this tutorial has given you a better understanding of DelphiFMX and how to use it with FastAPI to build apps that are production-ready by default and offer the finest developer experience.
What are some FAQs about APIs related to creating a Python GUI and asynchronous service?
What are some benefits of asynchronous programming?
Using async programming improves performance, responsiveness, and user experience. With it, following page refreshes are no longer required because page load delays are eliminated.
Furthermore, even if other requests are running, you can use more than one feature at a time. Also, asynchronous applications use few resources and are very scalable, and the time it takes to respond to one request has no bearing on the time it takes to respond to others.
Another important aspect is that when one thread fails, the others keep rendering. It is also beneficial that the developer can make custom error messages using the built-in callback.
What is Embarcadero?
Embarcadero tools are designed for the world’s most elite developers who create and maintain the world’s most critical applications.
Embarcadero specializes in building business-critical applications in a demanding vertical scaling. It allows users to write steadfast code quickly and pass stringent code reviews faster than any other. In addition, it provides help from elite developers who understand C++ and Delphi’s scalability and stability and rely on the decades of innovation those languages bring to development.
Why is DelphiVCL famous in Python GUI?
DelphiVCL for Python is a widely known and recognized Python GUI framework geared toward native Windows development. It uses the Windows-only VCL framework and brings a powerful, flexible GUI framework to Windows, Linux, macOS, and Android.
What makes FastAPI one of the best libraries for creating apps in Python?
Many reasons make FastAPI one of the best APIs for creating production-ready applications. The first is the speed of development. Autocompletion and type hints in your favorite IDE or code editor are available in FastAPI. This is because the framework was designed to reduce the developer’s workload. Additional plugins, such as Flask-Marshmallow, are not required for FastAPI validation and serialization.
Secondly, the speed of execution: is fast too. FastAPI is capable of async execution. The server and toolkit it is based on are thanks to Uvicorn and Starlette. Flask lacks that capability as of now, giving FastAPI an advantage.
I’m getting an error in the fastapi library:
This is the utils.py sentence:
try:
return response_field(field_info=field_info)
except RuntimeError:
raise fastapi.exceptions.FastAPIError(
“Invalid args for response field! Hint: ”
f”check that {type_} is a valid Pydantic field type. ”
“If you are using a return type annotation that is not a valid Pydantic ”
“field (e.g. Union[Response, dict, None]) you can disable generating the ”
“response model from the type annotation with the path operation decorator ”
“parameter response_model=None. Read more: ”
“https://fastapi.tiangolo.com/tutorial/response-model/”
) from None
And this is the error when I run the app.py
>>>
*** Remote Interpreter Reinitialized ***
Traceback (most recent call last):
File “D:\PruebasPython\AEMS\PythonBlogExamples-main\Async_FastAPI_GUI_client\app.py”, line 37, in
@app.get(“/employees”)
^^^^^^^^^^^^^^^^^^^^^
File “C:\Users\Usuario\AppData\Local\Programs\Python\Python311\Lib\site-packages\fastapi\routing.py”, line 657, in decorator
self.add_api_route(
File “C:\Users\Usuario\AppData\Local\Programs\Python\Python311\Lib\site-packages\fastapi\routing.py”, line 596, in add_api_route
route = route_class(
^^^^^^^^^^^^
File “C:\Users\Usuario\AppData\Local\Programs\Python\Python311\Lib\site-packages\fastapi\routing.py”, line 405, in __init__
self.response_field = create_response_field(
^^^^^^^^^^^^^^^^^^^^^^
File “C:\Users\Usuario\AppData\Local\Programs\Python\Python311\Lib\site-packages\fastapi\utils.py”, line 90, in create_response_field
raise fastapi.exceptions.FastAPIError(
fastapi.exceptions.FastAPIError: Invalid args for response field! Hint: check that typing.List[employee.Employee] is a valid Pydantic field type. If you are using a return type annotation that is not a valid Pydantic field (e.g. Union[Response, dict, None]) you can disable generating the response model from the type annotation with the path operation decorator parameter response_model=None. Read more: https://fastapi.tiangolo.com/tutorial/response-model/
>>>
First of all, thank you for your interest in our GUI packages. The issue might be that you’re using Python 3.11 version, which I can see from the new error descriptions of 3.11. DelphiFMX supports 3.6 through 3.10 only officially yet. Please create a new virtual environment with other supported Python versions and try it. If faced with any further issues, please comment here.
OK, Thks!! I’ll try that.
I’ve installed Python 3.10 and I’m having the same exact problem.
When I run app.py from pyscrypter I’m having this issue:
Traceback (most recent call last):
File “D:\PruebasPython\AEMS\PythonBlogExamples-main\Async_FastAPI_GUI_client\app.py”, line 38, in
async def get_all_employees() -> List[Employee]:
File “C:\Users\Usuario\AppData\Local\Programs\Python\Python310\lib\site-packages\fastapi\routing.py”, line 657, in decorator
self.add_api_route(
File “C:\Users\Usuario\AppData\Local\Programs\Python\Python310\lib\site-packages\fastapi\routing.py”, line 596, in add_api_route
route = route_class(
File “C:\Users\Usuario\AppData\Local\Programs\Python\Python310\lib\site-packages\fastapi\routing.py”, line 405, in __init__
self.response_field = create_response_field(
File “C:\Users\Usuario\AppData\Local\Programs\Python\Python310\lib\site-packages\fastapi\utils.py”, line 90, in create_response_field
raise fastapi.exceptions.FastAPIError(
fastapi.exceptions.FastAPIError: Invalid args for response field! Hint: check that typing.List[employee.Employee] is a valid Pydantic field type. If you are using a return type annotation that is not a valid Pydantic field (e.g. Union[Response, dict, None]) you can disable generating the response model from the type annotation with the path operation decorator parameter response_model=None. Read more: https://fastapi.tiangolo.com/tutorial/response-model/
I’m unable to simulate this issue, and it works perfectly on my end. This error is related to FastAPI. Please try installing miniconda and create a conda environment and try once. I can also suggest you try the same on another computer once and check whether it’s working.