-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Admin/CRUD: Unfriendly 500 Error if field is empty but required #261
Comments
@shattl Yeah, I came across a similar issue recently. If you upgrade to the latest version of We currently catch a few asyncpg errors, but will add more over time: |
@dantownsend here I am talking about not filling the fields by user -- and this fields we already know are required IF the admin front-end would be look after required fields, server never gets this erroneous request in the normal conditions -- |
It would be better if the front end caught it. If you add class MyTable(Table):
my_column = Varchar(required=True) Then the Pydantic model should catch that it wasn't provided. That way it shouldn't reach the database. We can look at adding some validation to the front end. |
@dantownsend Unfortunately (I hope I'm doing something wrong), Piccolo Admin currently doesn't handle required fields at all. If you add a required field to the We could fix this on frontend by adding an HTML |
@sinisaos The problem could just be |
@dantownsend Yes. The problem is |
@sinisaos We might have to make it so when |
There is a Django way to save a few man-years ) never consider empty string as valid value (for Numeric if used "default=None" option, admin panel says "value is not a valid decimal", but should say "field required" instead and even not start validation process // no matter decimal or non-decimal, it's empty) |
@shattl Piccolo Admin uses Pydantic for validation and in this case it is the correct behavior (at least that's how I understand it, if I'm wrong, sorry) and for Pydantic @dantownsend This can be done by writing a custom validator for the empty string in # piccolo orm pydantic.py
def pydantic_empty_string_validator(cls, value):
if value == "":
raise ValueError("Required field cannot be empty")
return value
# and later use validator
elif isinstance(column, Varchar):
if not column._meta.required:
value_type = pydantic.constr(max_length=column.length)
else:
value_type = column.value_type
validators[
f"{column_name}_is_empty_string"
] = pydantic.validator(column_name, allow_reuse=True)(
pydantic_empty_string_validator
)
# in test_pydantic.py
def test_varchar_required(self):
class Director(Table):
name = Varchar(length=10, required=True)
pydantic_model = create_pydantic_model(table=Director)
with self.assertRaises(ValidationError):
pydantic_model(name="")
pydantic_model(name="non-empty field") We can also do frontend validation if you want with the HTML required tag (although it can be bypassed, so I don't think it makes much sense). In Piccolo Admin it works and returns |
Hi! We get this red message in Admin panel while adding model entity:
"Error
Internal server error"
I wish there would be message in front of the field saying this field is required instead of some abstract server error
In the other case if saving with all empty fields, message is like this:
Traceback (most recent call last): File "/usr/local/lib/python3.10/site-packages/anyio/streams/memory.py", line 94, in receive return self.receive_nowait() File "/usr/local/lib/python3.10/site-packages/anyio/streams/memory.py", line 89, in receive_nowait raise WouldBlock anyio.WouldBlock During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/local/lib/python3.10/site-packages/starlette/middleware/base.py", line 77, in call_next message = await recv_stream.receive() File "/usr/local/lib/python3.10/site-packages/anyio/streams/memory.py", line 114, in receive raise EndOfStream anyio.EndOfStream During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 162, in __call__ await self.app(scope, receive, _send) File "/usr/local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 79, in __call__ raise exc File "/usr/local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 68, in __call__ await self.app(scope, receive, sender) File "/usr/local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__ raise e File "/usr/local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__ await self.app(scope, receive, send) File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 706, in __call__ await route.handle(scope, receive, send) File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 443, in handle await self.app(scope, receive, send) File "/usr/local/lib/python3.10/site-packages/fastapi/applications.py", line 270, in __call__ await super().__call__(scope, receive, send) File "/usr/local/lib/python3.10/site-packages/starlette/applications.py", line 124, in __call__ await self.middleware_stack(scope, receive, send) File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 174, in __call__ response = await self.handler(request, exc) File "/usr/local/lib/python3.10/site-packages/piccolo_admin/endpoints.py", line 331, in log_error raise exc File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 162, in __call__ await self.app(scope, receive, _send) File "/usr/local/lib/python3.10/site-packages/starlette/middleware/base.py", line 106, in __call__ response = await self.dispatch_func(request, call_next) File "/usr/local/lib/python3.10/site-packages/piccolo_api/csrf/middleware.py", line 179, in dispatch return await call_next(request) File "/usr/local/lib/python3.10/site-packages/starlette/middleware/base.py", line 80, in call_next raise app_exc File "/usr/local/lib/python3.10/site-packages/starlette/middleware/base.py", line 69, in coro await self.app(scope, receive_or_disconnect, send_no_error) File "/usr/local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 79, in __call__ raise exc File "/usr/local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 68, in __call__ await self.app(scope, receive, sender) File "/usr/local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__ raise e File "/usr/local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__ await self.app(scope, receive, send) File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 706, in __call__ await route.handle(scope, receive, send) File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 443, in handle await self.app(scope, receive, send) File "/usr/local/lib/python3.10/site-packages/starlette/middleware/authentication.py", line 48, in __call__ await self.app(scope, receive, send) File "/usr/local/lib/python3.10/site-packages/fastapi/applications.py", line 270, in __call__ await super().__call__(scope, receive, send) File "/usr/local/lib/python3.10/site-packages/starlette/applications.py", line 124, in __call__ await self.middleware_stack(scope, receive, send) File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 174, in __call__ response = await self.handler(request, exc) File "/usr/local/lib/python3.10/site-packages/piccolo_admin/endpoints.py", line 331, in log_error raise exc File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 162, in __call__ await self.app(scope, receive, _send) File "/usr/local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 79, in __call__ raise exc File "/usr/local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 68, in __call__ await self.app(scope, receive, sender) File "/usr/local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__ raise e File "/usr/local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__ await self.app(scope, receive, send) File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 706, in __call__ await route.handle(scope, receive, send) File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 276, in handle await self.app(scope, receive, send) File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 66, in app response = await func(request) File "/usr/local/lib/python3.10/site-packages/fastapi/routing.py", line 235, in app raw_response = await run_endpoint_function( File "/usr/local/lib/python3.10/site-packages/fastapi/routing.py", line 161, in run_endpoint_function return await dependant.call(**values) File "/usr/local/lib/python3.10/site-packages/piccolo_api/fastapi/endpoints.py", line 280, in post return await piccolo_crud.root(request=request) File "/usr/local/lib/python3.10/site-packages/piccolo_api/crud/endpoints.py", line 513, in root return await self.post_single(request, data) File "/app/apps/admin/patches.py", line 48, in inner_coroutine_function return await func(*args, **kwargs) File "/usr/local/lib/python3.10/site-packages/piccolo_api/crud/exceptions.py", line 52, in inner return await func(*args, **kwargs) File "/usr/local/lib/python3.10/site-packages/piccolo_api/crud/endpoints.py", line 819, in post_single response = await row.save().run() File "/usr/local/lib/python3.10/site-packages/piccolo/query/base.py", line 203, in run results = await engine.run_querystring( File "/usr/local/lib/python3.10/site-packages/piccolo/engine/postgres.py", line 458, in run_querystring return await self._run_in_pool(query, query_args) File "/usr/local/lib/python3.10/site-packages/piccolo/engine/postgres.py", line 423, in _run_in_pool response = await connection.fetch(query, *args) File "/usr/local/lib/python3.10/site-packages/asyncpg/connection.py", line 621, in fetch return await self._execute( File "/usr/local/lib/python3.10/site-packages/asyncpg/connection.py", line 1659, in _execute result, _ = await self.__execute( File "/usr/local/lib/python3.10/site-packages/asyncpg/connection.py", line 1684, in __execute return await self._do_execute( File "/usr/local/lib/python3.10/site-packages/asyncpg/connection.py", line 1731, in _do_execute result = await executor(stmt, None) File "asyncpg/protocol/protocol.pyx", line 201, in bind_execute asyncpg.exceptions.NotNullViolationError: null value in column "model" of relation "car" violates not-null constraint DETAIL: Failing row contains (6293232c-58db-4879-9b66-e9f591c8adf9, null, 0, , , 0.000000, 0.000000, null, , 0.00, , 0, null, 0.00, 0.00, 0.00, 5, 4, t, 0).
The text was updated successfully, but these errors were encountered: