Comments (15)
@jrycw I also think it's an easy way (hiding the unique column field) to prevent admin errors. Bulk updating unique columns doesn't make sense because it breaks unique constraints (we have that case now because we can only update the first row, but already the next row raise error because unique constraints are violated), but we'll have to wait and see what Daniel says about all this.
from piccolo_api.
Interesting. I haven't tried bulk updating the BaseUser
table. I'll give it a go.
from piccolo_api.
I just gave it a go, and I can see what you mean. A 500 error was being raised.
I've tried out your suggested fix, and it all seems to work great.
from piccolo_api.
Do you mind creating a PR with the changes?
I'm happy with either method. Lets go with this as it's a bit shorter:
if issubclass(cls, BaseUser):
if password := values.pop("password", None):
cls._validate_password(password)
values["password"] = cls.hash_password(password)
I noticed that we don't handle the potential exception raised by cls._validate_password(password)
. For example, if the password is too short. It would be nice to return a 400 error.
from piccolo_api.
Do you mind creating a PR with the changes?
I'm happy with either method. Lets go with this as it's a bit shorter:
if issubclass(cls, BaseUser): if password := values.pop("password", None): cls._validate_password(password) values["password"] = cls.hash_password(password)I noticed that we don't handle the potential exception raised by
cls._validate_password(password)
. For example, if the password is too short. It would be nice to return a 400 error.
@dantownsend Absolutely, I'm more than willing to submit a PR. However, if we attempt to handle the exception as follows:
if issubclass(cls, BaseUser):
if password := values.pop("password", None):
try:
cls._validate_password(password)
except ValueError as e:
return Response(f"{e}", status_code=400)
values["password"] = cls.hash_password(password)
it seems that test_patch_user_fails
won't pass, as we're handling the exception and returning a response instead of raising it.
def test_patch_user_fails(self):
...
with self.assertRaises(ValueError):
response = client.patch(f"/{user['id']}/", json=json)
> self.assertEqual(response.content, b"The password is too short.")
E AssertionError: ValueError not raised
from piccolo_api.
I just gave it a go, and I can see what you mean. A 500 error was being raised.
I've tried out your suggested fix, and it all seems to work great.
The fix should ensure that patch_single
behaves correctly and addresses potential issues with updating and bulk updating (for fields not subject to unique constraints) using the admin panel.
However, when bulk updating fields subject to unique constraints (across any tables, not just BaseUser
) via the admin panel, the first row is successfully updated, but subsequent rows fail. This behavior shouldn't be attributed to patch_single
, as it functions correctly. One potential solution is to filter out fields subject to unique constraints in advance within the admin panel. However, it seems this issue is related to piccolo_admin
and may require further investigation.
from piccolo_api.
However, when bulk updating fields subject to unique constraints (across any tables, not just
BaseUser
) via the admin panel, the first row is successfully updated, but subsequent rows fail. This behavior shouldn't be attributed topatch_single
, as it functions correctly. One potential solution is to filter out fields subject to unique constraints in advance within the admin panel. However, it seems this issue is related topiccolo_admin
and may require further investigation.
This can be solved by adding a unique
argument to an column extra in pydantic schema like this
# in piccolo/utils/pydantic.py
extra: JsonDict = {
"help_text": column._meta.help_text,
"choices": column._meta.get_choices_dict(),
"secret": column._meta.secret,
"nullable": column._meta.null,
"unique": column._meta.unique, # new argument
}
After that we can simply not show unique column in Bulkupdate
dropdown like this
// in BulkUpdateModal.vue
<template v-for="(property, columnName) in schema.properties">
<option
:key="columnName"
:value="columnName"
v-if="
columnName != schema.extra.primary_key_name &&
property.extra.unique == false // check if column is unique
"
>
{{ property.title }}
</option>
</template>
This will disable the attempt to update in bulk unique columns. Any thoughts?
from piccolo_api.
However, when bulk updating fields subject to unique constraints (across any tables, not just
BaseUser
) via the admin panel, the first row is successfully updated, but subsequent rows fail. This behavior shouldn't be attributed topatch_single
, as it functions correctly. One potential solution is to filter out fields subject to unique constraints in advance within the admin panel. However, it seems this issue is related topiccolo_admin
and may require further investigation.This can be solved by adding a
unique
argument to an column extra in pydantic schema like this# in piccolo/utils/pydantic.py extra: JsonDict = { "help_text": column._meta.help_text, "choices": column._meta.get_choices_dict(), "secret": column._meta.secret, "nullable": column._meta.null, "unique": column._meta.unique, # new argument }After that we can simply not show unique column in
Bulkupdate
dropdown like this// in BulkUpdateModal.vue <template v-for="(property, columnName) in schema.properties"> <option :key="columnName" :value="columnName" v-if=" columnName != schema.extra.primary_key_name && property.extra.unique == false // check if column is unique " > {{ property.title }} </option> </template>
This will disable the attempt to update in bulk unique columns. Any thoughts?
@sinisaos I consider this approach to be beneficial because by filtering the fields subjected to unique constraints in advance while bulk updating, we can offer the best user experience for those using the admin panel. If other users are dissatisfied with this approach, they still have the option to build the interface they desire using piccolo_api
directly.
from piccolo_api.
I agree that filtering out unique columns from the bulk update dropdown makes a lot of sense. The solution that @sinisaos put forward looks good.
@dantownsend Absolutely, I'm more than willing to submit a PR. However, if we attempt to handle the exception as follows:
if issubclass(cls, BaseUser): if password := values.pop("password", None): try: cls._validate_password(password) except ValueError as e: return Response(f"{e}", status_code=400) values["password"] = cls.hash_password(password)
it seems that test_patch_user_fails won't pass, as we're handling the exception and returning a response instead of raising it.
def test_patch_user_fails(self): ... with self.assertRaises(ValueError): response = client.patch(f"/{user['id']}/", json=json)
self.assertEqual(response.content, b"The password is too short.")
E AssertionError: ValueError not raised
Your changes look good. Can we refactor the test to remove the with self.assertRaises(ValueError):
?
from piccolo_api.
If I understand correctly, instead of raising a ValueError
, we're considering refactoring the test to expect a 400 response if "the password is too short".
The test_patch_user_fails
would then look like this:
def test_patch_user_fails(self):
client = TestClient(PiccoloCRUD(table=BaseUser, read_only=False))
json = {
"username": "John",
"password": "John123",
"email": "[email protected]",
"active": False,
"admin": False,
"superuser": False,
}
response = client.post("/", json=json)
self.assertEqual(response.status_code, 201)
user = BaseUser.select().first().run_sync()
json = {
"email": "[email protected]",
"password": "1",
"active": True,
"admin": True,
"superuser": False,
}
response = client.patch(f"/{user['id']}/", json=json)
self.assertEqual(response.status_code, 400)
self.assertEqual(response.content, b"The password is too short.")
@dantownsend If the changes to the code and test suit your expectations, I'll proceed with submitting a PR.
from piccolo_api.
@jrycw Yes, that looks good to me - thanks!
from piccolo_api.
@dantownsend If you want, I can do a unique column PR for Piccolo and Piccolo Admin in the evening?
from piccolo_api.
@sinisaos That would be good, thanks!
from piccolo_api.
@jrycw Can you please close this issue as all bugs from this issue are now resolved. Thanks.
from piccolo_api.
@sinisaos Sure, thank you once again for your assistance!
from piccolo_api.
Related Issues (20)
- Feature to modify body and query_parameters in `PiccoloCRUD` HOT 8
- Fix code scanning alert - Unused global variable
- Batch version of the get endpoint HOT 6
- Drop Python 3.7 support
- Catch foreign key constraint errors in `PiccoloCRUD`
- `schema_extra` param not passed to `pydantic_model_{output | optional | plural}` methods HOT 1
- Piccolo Admin API docs are not rendering
- Better error display on PATCH request
- Alpha version for Pydantic 2.0 / Piccolo 1.0a1? HOT 2
- PiccoloCRUD `post_single` return id of the inserted row instead of the row HOT 3
- Add `ne` operator
- Python 3.12 support
- RateLimitingMiddleware tests failed HOT 5
- Stop multi-dimensional arrays from breaking
- Updating middleware syntax HOT 6
- Make `default-src` configurable in `CSPMiddleware`
- Hide parameter in Validators to hide Piccolo Admin table link from sidebar if the validators fail. HOT 8
- Add `excluded_paths` to `SessionsAuthBackend`
- Replace deprecated `abstractproperty`
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from piccolo_api.