-
Notifications
You must be signed in to change notification settings - Fork 271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: 'Bill Type: Invalid Choice: could not coerce' error #1294
Conversation
ihatemoney/models.py
Outdated
|
||
class BillType(Enum): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a FormEnum
helper here
ihatemoney/ihatemoney/utils.py
Lines 324 to 344 in 4af4c10
class FormEnum(Enum): | |
"""Extend builtin Enum class to be seamlessly compatible with WTForms""" | |
@classmethod | |
def choices(cls): | |
return [(choice, choice.name) for choice in cls] | |
@classmethod | |
def coerce(cls, item): | |
"""Coerce a str or int representation into an Enum object""" | |
if isinstance(item, cls): | |
return item | |
# If item is not already a Enum object then it must be | |
# a string or int corresponding to an ID (e.g. '0' or 1) | |
# Either int() or cls() will correctly throw a TypeError if this | |
# is not the case | |
return cls(int(item)) | |
def __str__(self): | |
return str(self.value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed I also found this while adding the enum, but I wanted to have an Enum with string values to easily show a "nice" string to the user.
ihatemoney/models.py
Outdated
@classmethod | ||
def coerce(cls, item): | ||
try: | ||
return item if isinstance(item, BillType) else BillType[item.upper()] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need item.upper()
because the API expects that bill_type
is passed as the enum value (i.e. "Expense" or "Reimbursement") but item
is "EXPENSE" or "REIMBURSEMENT" when its passed as string to coerce
.
I've been playing around with the changes in this PR. To me it seems like I don't really understand why, but I think the key change that fixes the error in the form is adding |
I quickly pushed my change to explain what I mean: See this commit. When I run the server with this change, I also don't get the error message. |
Okay, I figured out what's going on. The WTForms SelectField sends the different choices to the browser as strings (documented here. These are specified by That's why adding an explicit @classmethod
def choices(cls):
return [(choice.value, choice.value) for choice in cls] |
Thanks, I have updated the PR to use: @classmethod
def choices(cls):
return [(choice.value, choice.value) for choice in cls] |
That looks good to me! Ah nice, you also caught the duplicate tests, I just saw those today. |
Thanks for the fix. Our CI was failing, which means that unformatted code went through in the last PR. I have fixed the issue in #1296 and hopefully reorganized the CI to avoid this in the future. It does mean that you have to rebase and possibly fix conflicts, sorry about that. But we should then get a clearer view of the CI. |
fb0111c
to
f7bbe83
Compare
The testcases with the lower versions for dependency are failing with HTTP 400 errors. It means that the approach doesn't work with older version of some libraries (here, most probably wtforms) Does your solution rely on a undocumented behaviour in wtforms? If so, it's preferable to find another solution that will be less fragile over time. If there is really no other workable approach, you can try bumping the minimum version of wtforms. |
Very strange, as far as I can tell, this solution is not using anything exotic from wtforms. All api calls are following the documented way of working for 2.3.x of wtforms, which should correspond to the minimal version. What I think is strange is that this test is breaking now, but the minimal version checks in #1290 were fine. I can try replicating the problem when I have time later this week. |
I found the culprit I think. I believe the problem is related to a bug fixed in a PR of WTForms here. The changelog for 2.3.2 mentions that they fixed a bug in the Is see 2 solutions to this problem, either we do a minor version bump for WTForms from 2.3.1 --> 2.3.2, or we use the following workaround which also seems to do the trick: class BillType(Enum):
EXPENSE = "Expense"
REIMBURSEMENT = "Reimbursement"
def __str__(self):
return self.value
@classmethod
def choices(cls):
return [(choice, choice.value) for choice in cls] Using a BillType instance as the first member of the choice tuple seems to not trigger the validation bug, while the str method makes sure flasks sends a sensible value to the browser which can then be coerced back to the enum. By the way, sorry @rriski that you got involved in this weird can of worms from my implementation! |
Thanks for looking at it! If it's a bug in wtforms, then let's use the simplest solution: @rriski , can you bump the wtforms dependency to >=2.3.3 ? |
Error introduced in spiral-project#1290. Fixes spiral-project#1293. WTForms needs to be bumped to >=2.3.2 as it includes a fix to `SelectField` which is required for this change to work. See: - https://wtforms.readthedocs.io/en/3.1.x/changes/#version-2-3-2 - pallets-eco/wtforms#598
Sure, updated PR now. Thanks @TomRoussel for figuring out the issue. |
Error introduced in #1290. Fixes #1293. WTForms needs to be bumped to >=2.3.2
as it includes a fix to
SelectField
which is required for this change to work.See: