Movatterモバイル変換


[0]ホーム

URL:


Saltar a contenido
Join theFastAPI Cloud waiting list 🚀
Follow@fastapi onX (Twitter) to stay updated
FollowFastAPI onLinkedIn to stay updated
Subscribe to theFastAPI and friends newsletter 🎉
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor

Aplicaciones más grandes - Múltiples archivos

🌐 Traducción por IA y humanos

Esta traducción fue hecha por IA guiada por humanos. 🤝

Podría tener errores al interpretar el significado original, o sonar poco natural, etc. 🤖

Puedes mejorar esta traducciónayudándonos a guiar mejor al LLM de IA.

Versión en inglés

Si estás construyendo una aplicación o una API web, rara vez podrás poner todo en un solo archivo.

FastAPI proporciona una herramienta conveniente para estructurar tu aplicación manteniendo toda la flexibilidad.

Información

Si vienes de Flask, esto sería el equivalente a los Blueprints de Flask.

Un ejemplo de estructura de archivos

Digamos que tienes una estructura de archivos como esta:

.├── app│   ├── __init__.py│   ├── main.py│   ├── dependencies.py│   └── routers│   │   ├── __init__.py│   │   ├── items.py│   │   └── users.py│   └── internal│       ├── __init__.py│       └── admin.py

Consejo

Hay varios archivos__init__.py: uno en cada directorio o subdirectorio.

Esto es lo que permite importar código de un archivo a otro.

Por ejemplo, enapp/main.py podrías tener una línea como:

from app.routers import items
  • El directorioapp contiene todo. Y tiene un archivo vacíoapp/__init__.py, por lo que es un "paquete de Python" (una colección de "módulos de Python"):app.
  • Contiene un archivoapp/main.py. Como está dentro de un paquete de Python (un directorio con un archivo__init__.py), es un "módulo" de ese paquete:app.main.
  • También hay un archivoapp/dependencies.py, al igual queapp/main.py, es un "módulo":app.dependencies.
  • Hay un subdirectorioapp/routers/ con otro archivo__init__.py, por lo que es un "subpaquete de Python":app.routers.
  • El archivoapp/routers/items.py está dentro de un paquete,app/routers/, por lo que es un submódulo:app.routers.items.
  • Lo mismo conapp/routers/users.py, es otro submódulo:app.routers.users.
  • También hay un subdirectorioapp/internal/ con otro archivo__init__.py, por lo que es otro "subpaquete de Python":app.internal.
  • Y el archivoapp/internal/admin.py es otro submódulo:app.internal.admin.

La misma estructura de archivos con comentarios:

.├──app# "app" es un paquete de Python  ├──__init__.py# este archivo hace que "app" sea un "paquete de Python"  ├──main.py# módulo "main", por ejemplo import app.main  ├──dependencies.py# módulo "dependencies", por ejemplo import app.dependencies  └──routers# "routers" es un "subpaquete de Python"  ├──__init__.py# hace que "routers" sea un "subpaquete de Python"  ├──items.py# submódulo "items", por ejemplo import app.routers.items  └──users.py# submódulo "users", por ejemplo import app.routers.users  └──internal# "internal" es un "subpaquete de Python"  ├──__init__.py# hace que "internal" sea un "subpaquete de Python"  └──admin.py# submódulo "admin", por ejemplo import app.internal.admin

APIRouter

Digamos que el archivo dedicado solo a manejar usuarios es el submódulo en/app/routers/users.py.

Quieres tener laspath operations relacionadas con tus usuarios separadas del resto del código, para mantenerlo organizado.

Pero todavía es parte de la misma aplicación/web API deFastAPI (es parte del mismo "paquete de Python").

Puedes crear laspath operations para ese módulo usandoAPIRouter.

ImportarAPIRouter

Lo importas y creas una "instance" de la misma manera que lo harías con la claseFastAPI:

app/routers/users.py
fromfastapiimportAPIRouterrouter=APIRouter()@router.get("/users/",tags=["users"])asyncdefread_users():return[{"username":"Rick"},{"username":"Morty"}]@router.get("/users/me",tags=["users"])asyncdefread_user_me():return{"username":"fakecurrentuser"}@router.get("/users/{username}",tags=["users"])asyncdefread_user(username:str):return{"username":username}

Path operations conAPIRouter

Y luego lo usas para declarar tuspath operations.

Úsalo de la misma manera que usarías la claseFastAPI:

app/routers/users.py
fromfastapiimportAPIRouterrouter=APIRouter()@router.get("/users/",tags=["users"])asyncdefread_users():return[{"username":"Rick"},{"username":"Morty"}]@router.get("/users/me",tags=["users"])asyncdefread_user_me():return{"username":"fakecurrentuser"}@router.get("/users/{username}",tags=["users"])asyncdefread_user(username:str):return{"username":username}

Puedes pensar enAPIRouter como una clase "miniFastAPI".

Se soportan todas las mismas opciones.

Todos los mismosparameters,responses,dependencies,tags, etc.

Consejo

En este ejemplo, la variable se llamarouter, pero puedes nombrarla como quieras.

Vamos a incluir esteAPIRouter en la aplicación principal deFastAPI, pero primero, revisemos las dependencias y otroAPIRouter.

Dependencias

Vemos que vamos a necesitar algunas dependencias usadas en varios lugares de la aplicación.

Así que las ponemos en su propio módulodependencies (app/dependencies.py).

Ahora utilizaremos una dependencia simple para leer un headerX-Token personalizado:

app/dependencies.py
fromtypingimportAnnotatedfromfastapiimportHeader,HTTPExceptionasyncdefget_token_header(x_token:Annotated[str,Header()]):ifx_token!="fake-super-secret-token":raiseHTTPException(status_code=400,detail="X-Token header invalid")asyncdefget_query_token(token:str):iftoken!="jessica":raiseHTTPException(status_code=400,detail="No Jessica token provided")

Consejo

Estamos usando un header inventado para simplificar este ejemplo.

Pero en casos reales obtendrás mejores resultados usando lasutilidades de Seguridad integradas.

Otro módulo conAPIRouter

Digamos que también tienes los endpoints dedicados a manejar "items" de tu aplicación en el móduloapp/routers/items.py.

Tienespath operations para:

  • /items/
  • /items/{item_id}

Es toda la misma estructura que conapp/routers/users.py.

Pero queremos ser más inteligentes y simplificar un poco el código.

Sabemos que todas laspath operations en este módulo tienen el mismo:

  • Prefijo de path:/items.
  • tags: (solo una etiqueta:items).
  • responses extra.
  • dependencies: todas necesitan esa dependenciaX-Token que creamos.

Entonces, en lugar de agregar todo eso a cadapath operation, podemos agregarlo alAPIRouter.

app/routers/items.py
fromfastapiimportAPIRouter,Depends,HTTPExceptionfrom..dependenciesimportget_token_headerrouter=APIRouter(prefix="/items",tags=["items"],dependencies=[Depends(get_token_header)],responses={404:{"description":"Not found"}},)fake_items_db={"plumbus":{"name":"Plumbus"},"gun":{"name":"Portal Gun"}}@router.get("/")asyncdefread_items():returnfake_items_db@router.get("/{item_id}")asyncdefread_item(item_id:str):ifitem_idnotinfake_items_db:raiseHTTPException(status_code=404,detail="Item not found")return{"name":fake_items_db[item_id]["name"],"item_id":item_id}@router.put("/{item_id}",tags=["custom"],responses={403:{"description":"Operation forbidden"}},)asyncdefupdate_item(item_id:str):ifitem_id!="plumbus":raiseHTTPException(status_code=403,detail="You can only update the item: plumbus")return{"item_id":item_id,"name":"The great Plumbus"}

Como el path de cadapath operation tiene que empezar con/, como en:

@router.get("/{item_id}")asyncdefread_item(item_id:str):...

...el prefijo no debe incluir un/ final.

Así que, el prefijo en este caso es/items.

También podemos agregar una lista detags yresponses extra que se aplicarán a todas laspath operations incluidas en este router.

Y podemos agregar una lista dedependencies que se añadirá a todas laspath operations en el router y se ejecutarán/solucionarán por cada request que les haga.

Consejo

Nota que, al igual quedependencias en decoradores depath operations, ningún valor será pasado a tupath operation function.

El resultado final es que los paths de item son ahora:

  • /items/
  • /items/{item_id}

...como pretendíamos.

  • Serán marcados con una lista de tags que contiene un solo string"items".
  • Estos "tags" son especialmente útiles para los sistemas de documentación interactiva automática (usando OpenAPI).
  • Todos incluirán lasresponses predefinidas.
  • Todas estaspath operations tendrán la lista dedependencies evaluadas/ejecutadas antes de ellas.
  • Si también declaras dependencias en unapath operation específica,también se ejecutarán.
  • Las dependencias del router se ejecutan primero, luego lasdependencies en el decorador, y luego las dependencias de parámetros normales.
  • También puedes agregardependencias deSecurity conscopes.

Consejo

Tenerdependencies en elAPIRouter puede ser usado, por ejemplo, para requerir autenticación para un grupo completo depath operations. Incluso si las dependencias no son añadidas individualmente a cada una de ellas.

Revisa

Los parámetrosprefix,tags,responses, ydependencies son (como en muchos otros casos) solo una funcionalidad deFastAPI para ayudarte a evitar la duplicación de código.

Importar las dependencias

Este código vive en el móduloapp.routers.items, el archivoapp/routers/items.py.

Y necesitamos obtener la función de dependencia del móduloapp.dependencies, el archivoapp/dependencies.py.

Así que usamos un import relativo con.. para las dependencias:

app/routers/items.py
fromfastapiimportAPIRouter,Depends,HTTPExceptionfrom..dependenciesimportget_token_headerrouter=APIRouter(prefix="/items",tags=["items"],dependencies=[Depends(get_token_header)],responses={404:{"description":"Not found"}},)fake_items_db={"plumbus":{"name":"Plumbus"},"gun":{"name":"Portal Gun"}}@router.get("/")asyncdefread_items():returnfake_items_db@router.get("/{item_id}")asyncdefread_item(item_id:str):ifitem_idnotinfake_items_db:raiseHTTPException(status_code=404,detail="Item not found")return{"name":fake_items_db[item_id]["name"],"item_id":item_id}@router.put("/{item_id}",tags=["custom"],responses={403:{"description":"Operation forbidden"}},)asyncdefupdate_item(item_id:str):ifitem_id!="plumbus":raiseHTTPException(status_code=403,detail="You can only update the item: plumbus")return{"item_id":item_id,"name":"The great Plumbus"}

Cómo funcionan los imports relativos

Consejo

Si sabes perfectamente cómo funcionan los imports, continúa a la siguiente sección abajo.

Un solo punto., como en:

from.dependenciesimportget_token_header

significaría:

  • Partiendo en el mismo paquete en el que este módulo (el archivoapp/routers/items.py) habita (el directorioapp/routers/)...
  • busca el módulodependencies (un archivo imaginario enapp/routers/dependencies.py)...
  • y de él, importa la funciónget_token_header.

Pero ese archivo no existe, nuestras dependencias están en un archivo enapp/dependencies.py.

Recuerda cómo se ve nuestra estructura de aplicación/archivo:


Los dos puntos.., como en:

from..dependenciesimportget_token_header

significan:

  • Partiendo en el mismo paquete en el que este módulo (el archivoapp/routers/items.py) habita (el directorioapp/routers/)...
  • ve al paquete padre (el directorioapp/)...
  • y allí, busca el módulodependencies (el archivo enapp/dependencies.py)...
  • y de él, importa la funciónget_token_header.

¡Eso funciona correctamente! 🎉


De la misma manera, si hubiéramos usado tres puntos..., como en:

from...dependenciesimportget_token_header

eso significaría:

  • Partiendo en el mismo paquete en el que este módulo (el archivoapp/routers/items.py) habita (el directorioapp/routers/)...
  • ve al paquete padre (el directorioapp/)...
  • luego ve al paquete padre de ese paquete (no hay paquete padre,app es el nivel superior 😱)...
  • y allí, busca el módulodependencies (el archivo enapp/dependencies.py)...
  • y de él, importa la funciónget_token_header.

Eso se referiría a algún paquete arriba deapp/, con su propio archivo__init__.py, etc. Pero no tenemos eso. Así que, eso lanzaría un error en nuestro ejemplo. 🚨

Pero ahora sabes cómo funciona, para que puedas usar imports relativos en tus propias apps sin importar cuán complejas sean. 🤓

Agregar algunostags,responses, ydependencies personalizados

No estamos agregando el prefijo/items ni lostags=["items"] a cadapath operation porque los hemos añadido alAPIRouter.

Pero aún podemos agregarmástags que se aplicarán a unapath operation específica, y también algunasresponses extra específicas para esapath operation:

app/routers/items.py
fromfastapiimportAPIRouter,Depends,HTTPExceptionfrom..dependenciesimportget_token_headerrouter=APIRouter(prefix="/items",tags=["items"],dependencies=[Depends(get_token_header)],responses={404:{"description":"Not found"}},)fake_items_db={"plumbus":{"name":"Plumbus"},"gun":{"name":"Portal Gun"}}@router.get("/")asyncdefread_items():returnfake_items_db@router.get("/{item_id}")asyncdefread_item(item_id:str):ifitem_idnotinfake_items_db:raiseHTTPException(status_code=404,detail="Item not found")return{"name":fake_items_db[item_id]["name"],"item_id":item_id}@router.put("/{item_id}",tags=["custom"],responses={403:{"description":"Operation forbidden"}},)asyncdefupdate_item(item_id:str):ifitem_id!="plumbus":raiseHTTPException(status_code=403,detail="You can only update the item: plumbus")return{"item_id":item_id,"name":"The great Plumbus"}

Consejo

Esta última path operation tendrá la combinación de tags:["items", "custom"].

Y también tendrá ambas responses en la documentación, una para404 y otra para403.

ElFastAPI principal

Ahora, veamos el módulo enapp/main.py.

Aquí es donde importas y usas la claseFastAPI.

Este será el archivo principal en tu aplicación que conecta todo.

Y como la mayor parte de tu lógica ahora vivirá en su propio módulo específico, el archivo principal será bastante simple.

ImportarFastAPI

Importas y creas una claseFastAPI como normalmente.

Y podemos incluso declarardependencias globales que se combinarán con las dependencias para cadaAPIRouter:

app/main.py
fromfastapiimportDepends,FastAPIfrom.dependenciesimportget_query_token,get_token_headerfrom.internalimportadminfrom.routersimportitems,usersapp=FastAPI(dependencies=[Depends(get_query_token)])app.include_router(users.router)app.include_router(items.router)app.include_router(admin.router,prefix="/admin",tags=["admin"],dependencies=[Depends(get_token_header)],responses={418:{"description":"I'm a teapot"}},)@app.get("/")asyncdefroot():return{"message":"Hello Bigger Applications!"}

Importar elAPIRouter

Ahora importamos los otros submódulos que tienenAPIRouters:

app/main.py
fromfastapiimportDepends,FastAPIfrom.dependenciesimportget_query_token,get_token_headerfrom.internalimportadminfrom.routersimportitems,usersapp=FastAPI(dependencies=[Depends(get_query_token)])app.include_router(users.router)app.include_router(items.router)app.include_router(admin.router,prefix="/admin",tags=["admin"],dependencies=[Depends(get_token_header)],responses={418:{"description":"I'm a teapot"}},)@app.get("/")asyncdefroot():return{"message":"Hello Bigger Applications!"}

Como los archivosapp/routers/users.py yapp/routers/items.py son submódulos que son parte del mismo paquete de Pythonapp, podemos usar un solo punto. para importarlos usando "imports relativos".

Cómo funciona la importación

La sección:

from.routersimportitems,users

significa:

  • Partiendo en el mismo paquete en el que este módulo (el archivoapp/main.py) habita (el directorioapp/)...
  • busca el subpaqueterouters (el directorio enapp/routers/)...
  • y de él, importa el submóduloitems (el archivo enapp/routers/items.py) yusers (el archivo enapp/routers/users.py)...

El móduloitems tendrá una variablerouter (items.router). Este es el mismo que creamos en el archivoapp/routers/items.py, es un objetoAPIRouter.

Y luego hacemos lo mismo para el módulousers.

También podríamos importarlos así:

fromapp.routersimportitems,users

Información

La primera versión es un "import relativo":

from.routersimportitems,users

La segunda versión es un "import absoluto":

fromapp.routersimportitems,users

Para aprender más sobre Paquetes y Módulos de Python, leela documentación oficial de Python sobre Módulos.

Evitar colisiones de nombres

Estamos importando el submóduloitems directamente, en lugar de importar solo su variablerouter.

Esto se debe a que también tenemos otra variable llamadarouter en el submódulousers.

Si hubiéramos importado uno después del otro, como:

from.routers.itemsimportrouterfrom.routers.usersimportrouter

elrouter deusers sobrescribiría el deitems y no podríamos usarlos al mismo tiempo.

Así que, para poder usar ambos en el mismo archivo, importamos los submódulos directamente:

app/main.py
fromfastapiimportDepends,FastAPIfrom.dependenciesimportget_query_token,get_token_headerfrom.internalimportadminfrom.routersimportitems,usersapp=FastAPI(dependencies=[Depends(get_query_token)])app.include_router(users.router)app.include_router(items.router)app.include_router(admin.router,prefix="/admin",tags=["admin"],dependencies=[Depends(get_token_header)],responses={418:{"description":"I'm a teapot"}},)@app.get("/")asyncdefroot():return{"message":"Hello Bigger Applications!"}

Incluir losAPIRouters parausers yitems

Ahora, incluyamos losrouters de los submódulosusers yitems:

app/main.py
fromfastapiimportDepends,FastAPIfrom.dependenciesimportget_query_token,get_token_headerfrom.internalimportadminfrom.routersimportitems,usersapp=FastAPI(dependencies=[Depends(get_query_token)])app.include_router(users.router)app.include_router(items.router)app.include_router(admin.router,prefix="/admin",tags=["admin"],dependencies=[Depends(get_token_header)],responses={418:{"description":"I'm a teapot"}},)@app.get("/")asyncdefroot():return{"message":"Hello Bigger Applications!"}

Información

users.router contiene elAPIRouter dentro del archivoapp/routers/users.py.

Yitems.router contiene elAPIRouter dentro del archivoapp/routers/items.py.

Conapp.include_router() podemos agregar cadaAPIRouter a la aplicación principal deFastAPI.

Incluirá todas las rutas de ese router como parte de ella.

Detalles Técnicos

En realidad creará internamente unapath operation para cadapath operation que fue declarada en elAPIRouter.

Así, detrás de escena, funcionará como si todo fuera la misma única app.

Revisa

No tienes que preocuparte por el rendimiento al incluir routers.

Esto tomará microsegundos y solo sucederá al inicio.

Así que no afectará el rendimiento. ⚡

Incluir unAPIRouter con unprefix,tags,responses, ydependencies personalizados

Ahora, imaginemos que tu organización te dio el archivoapp/internal/admin.py.

Contiene unAPIRouter con algunaspath operations de administración que tu organización comparte entre varios proyectos.

Para este ejemplo será súper simple. Pero digamos que porque está compartido con otros proyectos en la organización, no podemos modificarlo y agregar unprefix,dependencies,tags, etc. directamente alAPIRouter:

app/internal/admin.py
fromfastapiimportAPIRouterrouter=APIRouter()@router.post("/")asyncdefupdate_admin():return{"message":"Admin getting schwifty"}

Pero aún queremos configurar unprefix personalizado al incluir elAPIRouter para que todas suspath operations comiencen con/admin, queremos asegurarlo con lasdependencies que ya tenemos para este proyecto, y queremos incluirtags yresponses.

Podemos declarar todo eso sin tener que modificar elAPIRouter original pasando esos parámetros aapp.include_router():

app/main.py
fromfastapiimportDepends,FastAPIfrom.dependenciesimportget_query_token,get_token_headerfrom.internalimportadminfrom.routersimportitems,usersapp=FastAPI(dependencies=[Depends(get_query_token)])app.include_router(users.router)app.include_router(items.router)app.include_router(admin.router,prefix="/admin",tags=["admin"],dependencies=[Depends(get_token_header)],responses={418:{"description":"I'm a teapot"}},)@app.get("/")asyncdefroot():return{"message":"Hello Bigger Applications!"}

De esa manera, elAPIRouter original permanecerá sin modificar, por lo que aún podemos compartir ese mismo archivoapp/internal/admin.py con otros proyectos en la organización.

El resultado es que, en nuestra app, cada una de laspath operations del móduloadmin tendrá:

  • El prefix/admin.
  • El tagadmin.
  • La dependenciaget_token_header.
  • La response418. 🍵

Pero eso solo afectará a eseAPIRouter en nuestra app, no en ningún otro código que lo utilice.

Así, por ejemplo, otros proyectos podrían usar el mismoAPIRouter con un método de autenticación diferente.

Incluir unapath operation

También podemos agregarpath operations directamente a la app deFastAPI.

Aquí lo hacemos... solo para mostrar que podemos 🤷:

app/main.py
fromfastapiimportDepends,FastAPIfrom.dependenciesimportget_query_token,get_token_headerfrom.internalimportadminfrom.routersimportitems,usersapp=FastAPI(dependencies=[Depends(get_query_token)])app.include_router(users.router)app.include_router(items.router)app.include_router(admin.router,prefix="/admin",tags=["admin"],dependencies=[Depends(get_token_header)],responses={418:{"description":"I'm a teapot"}},)@app.get("/")asyncdefroot():return{"message":"Hello Bigger Applications!"}

y funcionará correctamente, junto con todas las otraspath operations añadidas conapp.include_router().

Detalles Muy Técnicos

Nota: este es un detalle muy técnico que probablemente puedessimplemente omitir.


LosAPIRouters no están "montados", no están aislados del resto de la aplicación.

Esto se debe a que queremos incluir suspath operations en el esquema de OpenAPI y las interfaces de usuario.

Como no podemos simplemente aislarlos y "montarlos" independientemente del resto, laspath operations se "clonan" (se vuelven a crear), no se incluyen directamente.

Revisa la documentación automática de la API

Ahora, ejecuta tu app:

$fastapidevapp/main.py<span style="color: green;">INFO</span>:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

Y abre la documentación enhttp://127.0.0.1:8000/docs.

Verás la documentación automática de la API, incluyendo los paths de todos los submódulos, usando los paths correctos (y prefijos) y los tags correctos:

Incluir el mismo router múltiples veces con diferentesprefix

También puedes usar.include_router() múltiples veces con elmismo router usando diferentes prefijos.

Esto podría ser útil, por ejemplo, para exponer la misma API bajo diferentes prefijos, por ejemplo,/api/v1 y/api/latest.

Este es un uso avanzado que quizás no necesites realmente, pero está allí en caso de que lo necesites.

Incluir unAPIRouter en otro

De la misma manera que puedes incluir unAPIRouter en una aplicaciónFastAPI, puedes incluir unAPIRouter en otroAPIRouter usando:

router.include_router(other_router)

Asegúrate de hacerlo antes de incluirrouter en la app deFastAPI, para que laspath operations deother_router también se incluyan.


[8]ページ先頭

©2009-2026 Movatter.jp