Get the StringConstraints directly from the Identifier type
Problem:
FastAPI does not validate custom string build using Annotated[str, StringConstraints(...)]
Temporary solution My solution is to annotate the constrained type with Path again, then it seems to work:
from typing import Annotated
from fastapi import FastAPI, Path
from pydantic import StringConstraints
# In my case this is imported from a lib with pydantic but without fastapi,
# so it can't be changed to Annotated[str, Path(pattern=r"^[a-fA-F0-9]+$")]
Identifier = Annotated[str, StringConstraints(pattern=r"^[a-fA-F0-9]+$")]
app = FastAPI()
@app.get("/{some_id}")
async def get_something(some_id: Annotated[Identifier, Path()]):
return some_id
Feature request
Would be nice if fastapi would be able to get the constraint directly from the Identifier type without being required to add Annotated[Identifier, Path()].
Originally posted by @JasperJuergensen in https://github.com/tiangolo/fastapi/discussions/10105#discussioncomment-6901418
What the OP wants is this:
from typing import Annotated
from fastapi import FastAPI, Path
from pydantic import StringConstraints
Identifier = Annotated[str, StringConstraints(pattern=r"^[a-fA-F0-9]+$")]
app = FastAPI()
@app.get("/{some_id}")
async def get_something(some_id: Identifier):
return some_id
A similar feature request is to use pydantic.Field.
It is because missing the matadata when none of Annotated args is FieldInfo or Depends
I've commit a fix pr #10356 . Hope it's work
Isn't it pretty much the same as #10259 and #10109?
Seems it was fixed somewhere:
Updated: use code from this comment, not workaround
from typing import Annotated
from fastapi import FastAPI
from pydantic import StringConstraints
Identifier = Annotated[str, StringConstraints(pattern=r"^[a-fA-F0-9]+$")]
app = FastAPI()
@app.get("/{some_id}")
async def get_something(some_id: Identifier):
return some_id
# TESTS
from fastapi.testclient import TestClient
client = TestClient(app)
def test_ok():
resp = client.get("/ad")
assert resp.status_code == 200, resp.json()
def test_invalid():
resp = client.get("/a-d")
assert resp.status_code == 422
assert resp.json() == {
"detail": [
{
"type": "string_pattern_mismatch",
"loc": ["path", "some_id"],
"msg": "String should match pattern '^[a-fA-F0-9]+$'",
"input": "a-d",
"ctx": {"pattern": "^[a-fA-F0-9]+$"},
}
]
}
def test_schema():
resp = client.get("/openapi.json")
schema = resp.json()
assert schema["paths"]["/{some_id}"]["get"]["parameters"][0] == {
"name": "some_id",
"in": "path",
"required": True,
"schema": {"type": "string", "pattern": "^[a-fA-F0-9]+$", "title": "Some Id"},
}
What about pydantic.Field, as per the first comment here?
Sorry, I used wrong code in my previous comment 🤦 Updated code to test desirable solution instead of workaround.
What about pydantic.Field, as per the first comment here?
Not sure I understand this.. Could you give an example?
If you mean Identifier = Annotated[str, Field(pattern=r"^[a-fA-F0-9]+$")] then it works
As @YuriiMotov says, this seems to have been fixed, I tried with:
from typing import Annotated
from fastapi import FastAPI
from pydantic import Field, StringConstraints
Identifier = Annotated[str, Field(pattern=r"^[a-fA-F0-9]+$")]
Identifier2 = Annotated[str, StringConstraints(pattern=r"^[a-fA-F0-9]+$")]
app = FastAPI()
@app.get("/{some_id}")
async def get_something(some_id: Identifier):
return some_id
@app.get("/other/{some_id}")
async def get_other(some_id: Identifier2):
return some_id
And both endpoints, with Field and StringConstraints are working and validating the data. 🚀
I understand this is what was wanted, so I'll close this one now. ☕