-
-
Notifications
You must be signed in to change notification settings - Fork 528
-
There is currently some problem with that, if I had something like 200 and 403 responses I could write:
@router.get("/url", response={200: SomeSchema, 403: OtherSchema}, auth=...) def some_func(...): if no_perms: return 403, OtherSchema() ... data = ... return 200, [SomeSchema.model_validate(row) for row in data]
This will work fine, but if I use that:
@router.get("/url", response={200: list[SomeSchema], 403: OtherSchema}, auth=...) def some_func(...): if no_perms: return 403, OtherSchema() ... data = ... return 200, [SomeSchema.model_validate(row) for row in data]
It will complain about schema, as if it doesn't have proper fields, but it actually does, since it initiated and validated by Schema
that used Pydantic behind scene.
Additionally I find some loop problem here, I tried to specify tuple[...]
and list[...]
with different variations and if I put list[Literal[200] | list[SomeSchema]]
it will try to revalidate same output until it crashes, in my case it did loop for 6453 times based on console output and then get InternalServerError
and crashes.
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 1 comment 7 replies
-
@XCanG hard to tell - you need to provide code for your SomeSchema and OtherSchema
Beta Was this translation helpful? Give feedback.
All reactions
-
@XCanG can be ton of reasons - cannot guess..
on the other hand I see that you use model_validate
in responses - you do not have to do this as django-ninja does that automatically
here is a minimal working example of your initial example:
from ninja import NinjaAPI, Schema api = NinjaAPI() class SomeSchema(Schema): id: int name: str class OtherSchema(Schema): code: int message: str @api.get("/url", response={200: list[SomeSchema], 403: OtherSchema}) def some_func(request, action: str = ''): if action == "show_403": return 403, {"code": 42, "message": "some message"} data = [{'id': 1, 'name': 'John'}, {'id': 2, 'name': 'Sarah'}] return 200, data
200 reponse:
SCR-20250404-qlhx
403 path:
SCR-20250404-qlkeBeta Was this translation helpful? Give feedback.
All reactions
-
model_validate also should work - but you do not have to do that as you will end up double validation:
@api.get("/url", response={200: list[SomeSchema], 403: OtherSchema}) def some_func(request, action: str = ''): if action == "show_403": return 403, OtherSchema(code=42, message="some message") data = [{'id': 1, 'name': 'John'}, {'id': 2, 'name': 'Sarah'}] return 200, [SomeSchema.model_validate(row) for row in data]
Beta Was this translation helpful? Give feedback.
All reactions
-
Ah, didn't know about validation. But I used that because raw django response didn't work out by default.
Since your example seems to work for you, then I will provide more context. 403 used to block users without permissions so the model is actually very minimal (that one that I called OtherSchema):
class BooleanResponse(Schema): result: bool
But for the case of the list
schemas, I use it in various parts of the code, so in all places it didn't work for me. But example of one schema:
class Payment_Request(Schema): amount: Decimal = Field(..., ge=10_000, le=1_000_000) company: str = Field(..., max_length=510) address: str = Field(..., max_length=510) inn: str = Field(..., max_length=510) kpp: str = Field(..., max_length=510) rsch: str = Field(..., max_length=510) bn: str = Field(..., max_length=510) kpsch: str = Field(..., max_length=510) bik: str = Field(..., max_length=510) phone: str = Field(..., max_length=20) email: str = Field(..., max_length=255) class Seller_InvoiceDB(Payment_Request): id: int amount: Decimal seller: SellerDB date: datetime payment_confirmed: bool = Field(default=False) accepted_at: datetime | None = Field(default=None) accepted_by: UserDB | None = Field(default=None)
In this case it return {200: list[Seller_InvoiceDB], 403: BooleanResponse}
and an example of endpoint:
@router.get( "/invoices/{seller_id}", response={200: list[Seller_InvoiceDB], 403: BooleanResponse}, auth=header_key, ) @paginate def get_seller_invoices( request: HttpRequest, seller_id: int, ) -> tuple[Literal[200], Sequence[Seller_InvoiceDB]] | tuple[Literal[403], BooleanResponse]: if (extra := validate_permissions(request.user, (UserType.director, UserType.accountant))): return status.HTTP_403_FORBIDDEN, BooleanResponse(result=False) return status.HTTP_200_OK, [Seller_InvoiceDB.model_validate(inv) for inv in Seller_Invoice.objects.filter(seller_id=seller_id).order_by("-id")]
But I would say that mostly all this comes out to proper schema (OpenAPI and Swagger), rather than actual validation and this is the reason why I putting it in response
in the first place.
There sometimes cases where I had to work with the data before proceed to return it to requester, so I would like to have some solution that prevent double validation if I already returning Pydantic schemas.
Beta Was this translation helpful? Give feedback.
All reactions
-
@XCanG well I still do not understand from your description - what's the problem ? :)
Do you see some error ? do you need multiple statuses for your response ?
Beta Was this translation helpful? Give feedback.
All reactions
-
Could you isolate some small example without lot of fields where you can reproduce the "crash" ?
Beta Was this translation helpful? Give feedback.