-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
each_item
should iterate only at the top-level instead of going through nested items
#1933
Comments
Hello @kataev |
each_item
iterating through nested items instead of just the top-level list?each_item
should iterate only at the top-level instead of going through nested items
When using a validator with `each_item`, the items are all validated one by one. But if the items are also iterable the subitems would then be validated because the validator would be kept as it is. Now the validator passed to the items is changed and won't be propagated closes pydantic#1933
I can see it both ways. But my main problem is that this could well be a breaking change for some people who rely on this behaviour. Doesn't this strictly speaking, need to wait for v2? |
Good question @samuelcolvin. TBH I was so surprised when I saw this issue I really can't imagine this behaviour to be the expected one. Unfortunately it's always hard to make changes that could break someone case but I'm quite sure it's easily fixable. |
@samuelcolvin Good point, I can actually see existing behavior be something that's relied on.. I'm normally not a fan of "just add another kwarg" so adding some keyword like from typing import List
from pydantic import BaseModel, validator
class Thing(BaseModel):
things: List[List[str]]
# Existing behavior: operates on whole value for property
@validator("things", each_item=False)
def one(cls, val):
assert len(val) > 1, "List must have multiple things in it"
return val
# Existing behavior: iterates through nested lists of property
@validator("things", each_item=True)
def two(cls, val):
assert val, "Strings cannot be empty"
return val
# NEW BEHAVIOR: opt out of iterating through nested lists
@validator("things", each_item=True, validate_nested=False)
def three(cls, val):
assert len(val) > 1, "Nested list must have multiple things in it"
return val
# Invalid because of validator `one`
Thing(things=[["a", "b"]])
# Invalid because of validator `two`
Thing(things=[["a", ""], ["c", "d"]])
# Invalid because of validator `three`
# Currently can't do this without iterating manually in a validator with `each_item=False`
Thing(things=[["a", "b"], ["c"]])
# Valid
Thing(things=[["a", "b"], ["c", "d"]]) |
We've been bitten by this a few times. Any resolution probably also resolves #1255 just to link the issue |
Thanks for the other issue @daviskirk I linked it to my PR as well! |
Ah, thanks for linking @daviskirk - I searched prior to adding issue but didn't see that one. Should we close mine @samuelcolvin? |
Sorry for the slow reply
I think this is the most sensible solution. @PrettyWood if you update #1991 I'll merge it. |
* fix: check only first sublevel for validators with `each_item` When using a validator with `each_item`, the items are all validated one by one. But if the items are also iterable the subitems would then be validated because the validator would be kept as it is. Now the validator passed to the items is changed and won't be propagated closes #1933 * chore: add breaking change
Output of
python -c "import pydantic.utils; print(pydantic.utils.version_info())"
:I'm seeing what appears to be inconsistent behavior with
each_item=True
when I'm using a field that's a list of tuples.For instance, a 'normal' model illustrates what seems to be logical behavior.
But if the field is anything like
List[Tuple[str], str]]
, then behavior is absolutely different.The output from
validate_all_things
makes perfect sense - it's just the entire list passed in.I would expect, however, that
each_item
would do just what it implies, namely calling the validator on each item of the list no matter what the item is. Instead, it's operating on every value within each tuple within the list, and I cannot get it to perform in a way whereval == ("one", "two")
.Is this expected behavior? If so, why?
I mean, strings are iterable just like tuples, so why isn't
each_item
iterating over the characters of each string in the first case?The text was updated successfully, but these errors were encountered: