mirror of
https://github.com/fastapi/fastapi.git
synced 2026-06-15 11:01:13 -04:00
Compare commits
5 Commits
translate-
...
0.137.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a82e5f2fac | ||
|
|
edd1461589 | ||
|
|
b78c82262f | ||
|
|
e0f8cadf09 | ||
|
|
d8aad201eb |
@@ -7,6 +7,13 @@ hide:
|
||||
|
||||
## Latest Changes
|
||||
|
||||
## 0.137.1 (2026-06-15)
|
||||
|
||||
### Fixes
|
||||
|
||||
* 🚨 Fix typing checks for APIRoute. PR [#15765](https://github.com/fastapi/fastapi/pull/15765) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🐛 Fix bug, allow empty path in path operation in prefixless router. PR [#15763](https://github.com/fastapi/fastapi/pull/15763) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.137.0 (2026-06-14)
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -492,7 +492,9 @@ Daha fazla özellik içeren daha kapsamlı bir örnek için <a href="https://fas
|
||||
|
||||
### Uygulamanızı deploy edin (opsiyonel) { #deploy-your-app-optional }
|
||||
|
||||
FastAPI uygulamanızı tek bir komutla [FastAPI Cloud](https://fastapicloud.com)'a deploy edebilirsiniz. 🚀
|
||||
İsterseniz FastAPI uygulamanızı [FastAPI Cloud](https://fastapicloud.com)'a deploy edebilirsiniz; eğer henüz yapmadıysanız gidip bekleme listesine katılın. 🚀
|
||||
|
||||
Zaten bir **FastAPI Cloud** hesabınız varsa (bekleme listesinden sizi davet ettiysek 😉), uygulamanızı tek bir komutla deploy edebilirsiniz.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
@@ -508,8 +510,6 @@ Deploying to FastAPI Cloud...
|
||||
|
||||
</div>
|
||||
|
||||
CLI, FastAPI uygulamanızı otomatik olarak algılar ve cloud'a deploy eder. Giriş yapmadıysanız, kimlik doğrulama sürecini tamamlamak için tarayıcınız açılır.
|
||||
|
||||
Hepsi bu! Artık uygulamanıza bu URL'den erişebilirsiniz. ✨
|
||||
|
||||
#### FastAPI Cloud hakkında { #about-fastapi-cloud }
|
||||
|
||||
@@ -111,7 +111,7 @@ q: str | None = None
|
||||
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
|
||||
|
||||
|
||||
/// note | Not
|
||||
/// info | Bilgi
|
||||
|
||||
`Body`, `Query`, `Path` ve daha sonra göreceğiniz diğerleriyle aynı ek validasyon ve metadata parametrelerine de sahiptir.
|
||||
|
||||
@@ -126,7 +126,7 @@ Varsayılan olarak **FastAPI**, body'nin doğrudan bu modelin içeriği olmasın
|
||||
Ancak, ek body parametreleri tanımladığınızda olduğu gibi, `item` anahtarı olan bir JSON ve onun içinde modelin içeriğini beklemesini istiyorsanız, `Body`'nin özel parametresi olan `embed`'i kullanabilirsiniz:
|
||||
|
||||
```Python
|
||||
item: Annotated[Item, Body(embed=True)]
|
||||
item: Item = Body(embed=True)
|
||||
```
|
||||
|
||||
yani şöyle:
|
||||
|
||||
@@ -8,7 +8,7 @@ API'niz neredeyse her zaman bir **response** body göndermek zorundadır. Ancak
|
||||
|
||||
Bir **request** body tanımlamak için, tüm gücü ve avantajlarıyla [Pydantic](https://docs.pydantic.dev/) modellerini kullanırsınız.
|
||||
|
||||
/// note | Not
|
||||
/// info | Bilgi
|
||||
|
||||
Veri göndermek için şunlardan birini kullanmalısınız: `POST` (en yaygını), `PUT`, `DELETE` veya `PATCH`.
|
||||
|
||||
|
||||
@@ -24,13 +24,13 @@ Ancak `fastapi`'dan `Query`, `Path`, `Cookie` ve diğerlerini import ettiğinizd
|
||||
|
||||
///
|
||||
|
||||
/// note | Not
|
||||
/// info | Bilgi
|
||||
|
||||
Cookie'leri tanımlamak için `Cookie` kullanmanız gerekir, aksi halde parametreler query parametreleri olarak yorumlanır.
|
||||
|
||||
///
|
||||
|
||||
/// note | Not
|
||||
/// info | Bilgi
|
||||
|
||||
**Tarayıcılar cookie'leri** özel şekillerde ve arka planda işlediği için, **JavaScript**'in onlara dokunmasına kolayca izin **vermezler**.
|
||||
|
||||
|
||||
@@ -66,19 +66,19 @@ Interactive docs’ta şöyle kullanılacaktır:
|
||||
|
||||
<img src="/img/tutorial/path-operation-configuration/image02.png">
|
||||
|
||||
## Response Açıklaması { #response-description }
|
||||
## Response description { #response-description }
|
||||
|
||||
`response_description` parametresi ile response açıklamasını belirtebilirsiniz:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
|
||||
|
||||
/// note | Not
|
||||
/// info | Bilgi
|
||||
|
||||
`response_description` özellikle response’u ifade eder; `description` ise genel olarak *path operation*’ı ifade eder.
|
||||
|
||||
///
|
||||
|
||||
/// tip | İpucu
|
||||
/// check | Ek bilgi
|
||||
|
||||
OpenAPI, her *path operation* için bir response description zorunlu kılar.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
|
||||
|
||||
/// note | Not
|
||||
/// info | Bilgi
|
||||
|
||||
FastAPI, 0.95.0 sürümünde `Annotated` desteğini ekledi (ve bunu önermeye başladı).
|
||||
|
||||
@@ -56,7 +56,7 @@ Dolayısıyla fonksiyonunuzu şöyle tanımlayabilirsiniz:
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
|
||||
|
||||
Ancak şunu unutmayın: `Annotated` kullanırsanız bu problem olmaz; çünkü `Query()` veya `Path()` için fonksiyon parametresi default değerlerini kullanmıyorsunuz.
|
||||
Namun şunu unutmayın: `Annotated` kullanırsanız bu problem olmaz; çünkü `Query()` veya `Path()` için fonksiyon parametresi default değerlerini kullanmıyorsunuz.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
|
||||
|
||||
@@ -131,7 +131,7 @@ Ayrıca sayısal doğrulamalar da tanımlayabilirsiniz:
|
||||
* `lt`: `l`ess `t`han
|
||||
* `le`: `l`ess than or `e`qual
|
||||
|
||||
/// note | Not
|
||||
/// info | Bilgi
|
||||
|
||||
`Query`, `Path` ve ileride göreceğiniz diğer class'lar ortak bir `Param` class'ının alt class'larıdır.
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
İstemcinin upload edeceği dosyaları `File` kullanarak tanımlayabilirsiniz.
|
||||
|
||||
/// note | Not
|
||||
/// info | Bilgi
|
||||
|
||||
Upload edilen dosyaları alabilmek için önce [`python-multipart`](https://github.com/Kludex/python-multipart) yükleyin.
|
||||
|
||||
Bir [Sanal ortam](../virtual-environments.md) oluşturduğunuzdan, aktive ettiğinizden ve ardından paketi yüklediğinizden emin olun. Örneğin:
|
||||
Bir [virtual environment](../virtual-environments.md) oluşturduğunuzdan, aktive ettiğinizden ve ardından paketi yüklediğinizden emin olun. Örneğin:
|
||||
|
||||
```console
|
||||
$ pip install python-multipart
|
||||
@@ -28,7 +28,7 @@ Bunun nedeni, upload edilen dosyaların "form data" olarak gönderilmesidir.
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
|
||||
|
||||
/// note | Not
|
||||
/// info | Bilgi
|
||||
|
||||
`File`, doğrudan `Form`’dan türeyen bir sınıftır.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
`File` ve `Form` kullanarak aynı anda hem dosyaları hem de form alanlarını tanımlayabilirsiniz.
|
||||
|
||||
/// note | Not
|
||||
/// info | Bilgi
|
||||
|
||||
Yüklenen dosyaları ve/veya form verisini almak için önce [`python-multipart`](https://github.com/Kludex/python-multipart) paketini kurun.
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Bir response model tanımlayabildiğiniz gibi, herhangi bir *path operation* iç
|
||||
|
||||
`status_code` parametresi, HTTP status code'u içeren bir sayı alır.
|
||||
|
||||
/// note | Bilgi
|
||||
/// info | Bilgi
|
||||
|
||||
Alternatif olarak `status_code`, Python'un [`http.HTTPStatus`](https://docs.python.org/3/library/http.html#http.HTTPStatus)'ı gibi bir `IntEnum` da alabilir.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ Bununla birlikte **FastAPI** ile [pytest](https://docs.pytest.org/)'i doğrudan
|
||||
|
||||
## `TestClient` Kullanımı { #using-testclient }
|
||||
|
||||
/// note | Not
|
||||
/// info | Bilgi
|
||||
|
||||
`TestClient` kullanmak için önce [`httpx`](https://www.python-httpx.org)'i kurun.
|
||||
|
||||
@@ -141,7 +141,7 @@ Sonra testlerinizde aynısını uygularsınız.
|
||||
|
||||
Backend'e veri geçme hakkında daha fazla bilgi için (`httpx` veya `TestClient` kullanarak) [HTTPX dokümantasyonu](https://www.python-httpx.org)'na bakın.
|
||||
|
||||
/// note | Not
|
||||
/// info | Bilgi
|
||||
|
||||
`TestClient`'ın Pydantic model'lerini değil, JSON'a dönüştürülebilen verileri aldığını unutmayın.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
|
||||
|
||||
__version__ = "0.137.0"
|
||||
__version__ = "0.137.1"
|
||||
|
||||
from starlette import status as status
|
||||
|
||||
|
||||
@@ -1062,6 +1062,41 @@ def _populate_api_route_state(
|
||||
|
||||
|
||||
class APIRoute(routing.Route):
|
||||
stream_item_type: Any | None
|
||||
response_model: Any
|
||||
summary: str | None
|
||||
response_description: str
|
||||
deprecated: bool | None
|
||||
operation_id: str | None
|
||||
response_model_include: IncEx | None
|
||||
response_model_exclude: IncEx | None
|
||||
response_model_by_alias: bool
|
||||
response_model_exclude_unset: bool
|
||||
response_model_exclude_defaults: bool
|
||||
response_model_exclude_none: bool
|
||||
include_in_schema: bool
|
||||
response_class: type[Response] | DefaultPlaceholder
|
||||
dependency_overrides_provider: Any | None
|
||||
callbacks: list[BaseRoute] | None
|
||||
openapi_extra: dict[str, Any] | None
|
||||
generate_unique_id_function: Callable[[Any], str] | DefaultPlaceholder
|
||||
strict_content_type: bool | DefaultPlaceholder
|
||||
tags: list[str | Enum]
|
||||
responses: dict[int | str, dict[str, Any]]
|
||||
unique_id: str
|
||||
status_code: int | None
|
||||
response_field: ModelField | None
|
||||
stream_item_field: ModelField | None
|
||||
dependencies: list[params.Depends]
|
||||
description: str
|
||||
response_fields: dict[int | str, ModelField]
|
||||
dependant: Dependant
|
||||
_flat_dependant: Dependant
|
||||
_embed_body_fields: bool
|
||||
body_field: ModelField | None
|
||||
is_sse_stream: bool
|
||||
is_json_stream: bool
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
path: str,
|
||||
@@ -2435,9 +2470,16 @@ class APIRouter(routing.Router):
|
||||
"A path prefix must not end with '/', as the routes will start with '/'"
|
||||
)
|
||||
else:
|
||||
for r in _iter_included_route_candidates(router.routes):
|
||||
path = getattr(r, "path", None)
|
||||
name = getattr(r, "name", "unknown")
|
||||
for route, route_context in _iter_routes_with_context(router.routes):
|
||||
if route_context is None:
|
||||
path = getattr(route, "path", None)
|
||||
name = getattr(route, "name", "unknown")
|
||||
elif route_context.starlette_route is not None:
|
||||
path = getattr(route_context.starlette_route, "path", None)
|
||||
name = getattr(route_context.starlette_route, "name", "unknown")
|
||||
else:
|
||||
path = route_context.path
|
||||
name = route_context.name
|
||||
if path is not None and not path:
|
||||
raise FastAPIError(
|
||||
f"Prefix and path cannot be both empty (path operation: {name})"
|
||||
|
||||
@@ -2,6 +2,7 @@ from typing import Annotated, cast
|
||||
|
||||
import pytest
|
||||
from fastapi import APIRouter, Body, Depends, FastAPI, Request
|
||||
from fastapi.exceptions import FastAPIError
|
||||
from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse
|
||||
from fastapi.routing import (
|
||||
APIRoute,
|
||||
@@ -807,6 +808,60 @@ def test_no_prefix_include_validation_sees_effective_starlette_route_candidates(
|
||||
assert cast(Route, candidates[0]).path == "/child/items"
|
||||
|
||||
|
||||
def test_no_prefix_include_validation_sees_effective_api_route_path():
|
||||
leaf_router = APIRouter()
|
||||
|
||||
@leaf_router.get("")
|
||||
def read_items():
|
||||
return []
|
||||
|
||||
parent_router = APIRouter()
|
||||
parent_router.include_router(leaf_router, prefix="/items")
|
||||
|
||||
# for coverage
|
||||
candidates = list(_iter_included_route_candidates(parent_router.routes))
|
||||
assert cast(APIRoute, candidates[0]).path == ""
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(parent_router)
|
||||
client = TestClient(app)
|
||||
|
||||
response = client.get("/items")
|
||||
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == []
|
||||
|
||||
|
||||
def test_no_prefix_include_validation_sees_effective_starlette_route_path():
|
||||
def endpoint(request):
|
||||
return PlainTextResponse("ok")
|
||||
|
||||
child_router = APIRouter(routes=[Route("/items", endpoint, name="read_items")])
|
||||
parent_router = APIRouter()
|
||||
parent_router.include_router(child_router, prefix="/child")
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(parent_router)
|
||||
client = TestClient(app)
|
||||
|
||||
response = client.get("/child/items")
|
||||
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.text == "ok"
|
||||
|
||||
|
||||
def test_no_prefix_include_validation_rejects_empty_effective_api_route_path():
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("")
|
||||
def read_items(): # pragma: no cover
|
||||
return []
|
||||
|
||||
app = FastAPI()
|
||||
with pytest.raises(FastAPIError):
|
||||
app.include_router(router)
|
||||
|
||||
|
||||
def test_apirouter_matches_fallback_without_include_context():
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user