Nur die Werte einer Liste von Datensätzen aus FastAPI zurückgeben
Indem wir nur Werte anstelle von Wörterbüchern zurückgeben, minimieren wir den Umfang und die Zeit der Datenübertragung.
In Python ist alles eine Klasse, was bedeutet, dass die Modelldaten einem Wörterbuch ähnlich sind. Aber Wörterbücher haben Schlüssel. Und wenn Sie eine Liste mit vielen Wörterbüchern aus FastAPI zurückgeben, ist die Größe der Daten, Schlüssel und Werte, in der Regel viel mehr als doppelt so groß wie die der Werte. Größere Daten und mehr Zeit bedeuten, dass unsere Anwendung nicht sehr effizient ist und langsamer als nötig arbeitet. Es bedeutet auch, dass sie mehr Energie verbraucht, was bedeutet, dass sie nicht sehr nachhaltig ist (klingt gut ... igitt).
Im Folgenden werden zwei Möglichkeiten vorgestellt und verglichen, wie Daten aus FastAPI zurückgegeben werden können:
- Eine Liste von Wörterbüchern
- Eine Liste von tuples mit Wörterbuchwerten
Um die Sache noch spannender zu machen, besteht die Antwort aus einem "Meta"-Teil und einem "Daten"-Teil. Sie können dies selbst ausprobieren, den Code finden Sie unten. Wie immer laufe ich auf Ubuntu 22.04.
Die ListResponse-Klasse
Ich habe die ListResponse-Klasse bereits in einem früheren Beitrag erwähnt. Was wir zurückgeben wollen, sind zwei Elemente, "meta" und "data".
return {
'meta': ListMetaResponse(
page=1,
per_page=10,
count=count,
total=total,
),
'data': <list-of-dicts or list-of-tuples>,
}
Die Klasse ListResponse erstellt ein einzelnes Antwortobjekt, das die Klasse ListMetaResponse und das Datenmodell verwendet. Um sie zu verwenden:
response_model=ListResponse(Item)
1. Rückgabe einer Liste von Dictionaries
Das Modell entnehmen Sie bitte dem Dokument FastAPI 'Response Model - Return Type', siehe Links unten.
Wir verwenden das folgende Modell und die folgenden Elemente:
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []
items = [
{
'name': 'Portal Gun',
'price': 42.0
},
{
'name': 'Plumbus',
'price': 32.0
},
]
Die Rückgabe der Daten ist sehr einfach; ich werde Sie hier nicht damit belästigen.
2. Rückgabe einer Liste von tuples , die die Werte enthält
In diesem Fall verwenden wir eine tuple für die Werte eines Wörterbuchs. Wir können keine Liste verwenden, da es keine Python Typing-Unterstützung für positionale Elemente in einer Liste gibt. Die Werte des Wörterbuchs müssen an festen Positionen in der tuple platziert werden. Dies bedeutet, dass das Modell ist, siehe oben:
Tuple[str, Optional[str], float, Optional[float], Optional[list[str]]]
Um die Werte in den tuples zu bekommen, kommt es auf Ihre Anwendung an, woher die Daten kommen. Hier nehme ich die 'items' wie oben gezeigt an, das sind 'unvollständige' Wörterbücher. Wir können dies zu einer Liste von tuples auf folgende Weise verarbeiten:
# extract values from items
def get_item_values(item):
d = {
'name': None,
'description': None,
'price': None,
'tax': None,
'tags': None,
} | item
return tuple(d.values())
items_values = list(map(get_item_values, items))
Wenn die "Elemente" aus einer Datenbank stammen, können Sie alle Felder in der Abfrage auswählen. Das Ergebnis wird eine Liste von tuples sein, und es besteht keine Notwendigkeit für diese Verarbeitung.
Vergleich der Daten von JSON
1. Liste der Wörterbücher
Um die Daten von JSON zu erhalten, führen Sie diese in einem anderen Terminal aus:
curl http://127.0.0.1:8888/items
Ergebnis:
{"meta":{"page":1,"per_page":10,"count":2,"total":2},"data":[{"name":"Portal Gun","description":null,"price":42.0,"tax":null,"tags":[]},{"name":"Plumbus","description":null,"price":32.0,"tax":null,"tags":[]}]}
Und nur der Datenteil:
[{"name":"Portal Gun","description":null,"price":42.0,"tax":null,"tags":[]},{"name":"Plumbus","description":null,"price":32.0,"tax":null,"tags":[]}]
Anzahl der bytes der Daten: 148.
2. Liste der tuples mit den Werten
Um die Daten von JSON zu erhalten:
curl http://127.0.0.1:8888/items-values
Ergebnis:
{"meta":{"page":1,"per_page":10,"count":2,"total":2},"data":[["Portal Gun",null,42.0,null,null],["Plumbus",null,32.0,null,null]]}
Und nur der Datenteil:
[["Portal Gun",null,42.0,null,null],["Plumbus",null,32.0,null,null]]
Anzahl der bytes der Daten: 68.
Das ist eine Reduzierung um 54%!
Der Code
Nachfolgend finden Sie den Code, falls Sie ihn ausprobieren möchten. Erstellen Sie eine virtual environment, dann:
pip install fastapi
pip install uvicorn
Starten Sie die Anwendung:
python main.py
Um die Posten anzuzeigen, geben Sie in Ihren Browser ein:
http://127.0.0.1:8888/items
Um die Item-Werte anzuzeigen, geben Sie in Ihren Browser ein:
http://127.0.0.1:8888/items-values
Den Code:
# main.py
import datetime
from functools import lru_cache
from typing import Any, List, Optional, Tuple, Union
from fastapi import FastAPI, status as fastapi_status
from pydantic import BaseModel, Field, create_model as create_pydantic_model
import uvicorn
# see also:
# https://fastapi.tiangolo.com/tutorial/response-model
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []
items = [
{
'name': 'Portal Gun',
'price': 42.0
},
{
'name': 'Plumbus',
'price': 32.0
},
]
class ListMetaResponse(BaseModel):
page: int = Field(..., title='Pagination page number', example='2')
per_page: int = Field(..., title='Pagination items per page', example='10')
count: int = Field(..., title='Number of items returned', example='10')
total: int = Field(..., title='Total number of items', example='100')
class ListResponse(BaseModel):
def __init__(self, data_model=None):
pass
# KeyError when using the Pydantic model dynamically created by created_model in two Generic Model as response model #3464
# https://github.com/tiangolo/fastapi/issues/3464
@lru_cache(None)
def __new__(cls, data_model=None):
if hasattr(data_model, '__name__'):
data_model_name = data_model.__name__
else:
data_model_name = '???'
print(f'data_model_name = {data_model_name}')
return create_pydantic_model(
data_model_name + 'ListResponse',
meta=(ListMetaResponse, ...),
data=(List[data_model], ...),
__base__=BaseModel,
)
app = FastAPI()
@app.get('/')
def index():
return 'Hello index'
@app.get(
'/items',
name='Get items',
status_code=fastapi_status.HTTP_200_OK,
response_model=ListResponse(Item)
)
async def return_items() -> Any:
return {
'meta': ListMetaResponse(
page=1,
per_page=10,
count=len(items),
total=len(items),
),
'data': items
}
@app.get(
'/items-values',
name='Get item values',
status_code=fastapi_status.HTTP_200_OK,
response_model=ListResponse(Tuple[str, Optional[str], float, Optional[float], Optional[list[str]]]),
)
async def return_items_values() -> Any:
# extract values from items
def get_item_values(item):
d = {
'name': None,
'description': None,
'price': None,
'tax': None,
'tags': None,
} | item
return tuple(d.values())
items_values = list(map(get_item_values, items))
print(f'items_values = {items_values}')
return {
'meta': ListMetaResponse(
page=1,
per_page=10,
count=len(items),
total=len(items),
),
'data': items_values,
}
if __name__ == "__main__":
uvicorn.run("main:app", host='127.0.0.1', port=8888, reload=True)
Zusammenfassung
Die Verringerung der zu übertragenden Datenmenge macht Ihre Anwendung effizienter und reaktionsschneller. Python Typing unterstützt keine positionsbezogenen Elemente in einer Liste, was bedeutet, dass wir tuples verwenden. In vielen Fällen, z. B. bei der Auswahl von Daten aus einer Datenbank, benötigen wir keine Operation, um die Werte zu extrahieren, da die zurückgegebenen Zeilen bereits tuples sind.
Links / Impressum
FastAPI - Response Model - Return Type
https://fastapi.tiangolo.com/tutorial/response-model
Pydantic
https://docs.pydantic.dev/latest
Neueste
- Ausblenden der Primärschlüssel der Datenbank UUID Ihrer Webanwendung
- Don't Repeat Yourself (DRY) mit Jinja2
- SQLAlchemy, PostgreSQL, maximale Anzahl von Zeilen pro user
- Anzeige der Werte in den dynamischen Filtern SQLAlchemy
- Sichere Datenübertragung mit Public Key Verschlüsselung und pyNaCl
- rqlite: eine hochverfügbare und distverteilte SQLite -Alternative
Meistgesehen
- Verwendung von Pythons pyOpenSSL zur Überprüfung von SSL-Zertifikaten, die von einem Host heruntergeladen wurden
- Verwendung von UUIDs anstelle von Integer Autoincrement Primary Keys mit SQLAlchemy und MariaDb
- Verbindung zu einem Dienst auf einem Docker -Host von einem Docker -Container aus
- PyInstaller und Cython verwenden, um eine ausführbare Python-Datei zu erstellen
- SQLAlchemy: Verwendung von Cascade Deletes zum Löschen verwandter Objekte
- Flask RESTful API Validierung von Anfrageparametern mit Marshmallow-Schemas