Skip to content

@validates accepts multiple field names#1965

Merged
sloria merged 8 commits into
marshmallow-code:devfrom
dharani7998:feature/multiple_fields_in_validates
Mar 4, 2025
Merged

@validates accepts multiple field names#1965
sloria merged 8 commits into
marshmallow-code:devfrom
dharani7998:feature/multiple_fields_in_validates

Conversation

@dharani7998

Copy link
Copy Markdown
Contributor

Fix for #1960

Comment thread src/marshmallow/decorators.py
@dharani7998
dharani7998 requested a review from deckar01 March 31, 2022 15:25
@sloria

sloria commented Jan 16, 2025

Copy link
Copy Markdown
Member

apologies for the long delay on this. i hesitated initially because i had the same concern as @deckar01 about this being a breaking change. now that we're closing to releasing the next major, i think this is one that we can get merged. i'll update this when i have some time

@sloria sloria added this to the 4.0 milestone Jan 16, 2025
@sloria

sloria commented Jan 16, 2025

Copy link
Copy Markdown
Member

should validator methods receive field_name?

from marshmallow import Schema, fields, validates, ValidationError

class UserSchema(Schema):
    name = fields.Str(required=True)
    nickname = fields.Str(required=True)

    @validates("name", "nickname")
    def validate_names(self, value: str, field_name: str) -> str:
        if len(value) < 3:
            raise ValidationError(f"{field_name} too short")
        return value

@sloria

sloria commented Jan 16, 2025

Copy link
Copy Markdown
Member

looks like SQLAlchemy does pass a key argument: https://docs.sqlalchemy.org/en/20/orm/mapped_attributes.html#simple-validators

from sqlalchemy.orm import validates


class EmailAddress(Base):
    __tablename__ = "address"

    id = mapped_column(Integer, primary_key=True)
    email = mapped_column(String)

    @validates("email")
    def validate_email(self, key, address):
        if "@" not in address:
            raise ValueError("failed simple email validation")
        return address

@sloria sloria changed the title multiple fields in validates decorator @validates accepts multiple field names Jan 18, 2025
@sloria

sloria commented Jan 18, 2025

Copy link
Copy Markdown
Member

Updated to pass data_key as a keyword argument to decorated methods. Would like one review on this since it's a breaking change

@sloria
sloria requested a review from lafrech January 18, 2025 21:49

@lafrech lafrech left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No strong opinion about this. It adds a bit of API surface for a case that may not be so frequent, but addressing it with validates_schema is less convenient so this makes sense.

I'm not a fan of the "string or list of strings" pattern. In fact I didn't see where the magic operates but I suppose it does, otherwise all tests would be broken. And expecting a list of strings for a single field (99% case) would be a pity.

Comment thread docs/quickstart.rst
@validates("name", "nickname")
def validate_names(self, value: str, data_key: str) -> None:
if len(value) < 3:
raise ValidationError("Too short")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be the right place to show the use of data_key like in the upgrading guide.

@sloria

sloria commented Jan 27, 2025

Copy link
Copy Markdown
Member

I'm not a fan of the "string or list of strings" pattern. In fact I didn't see where the magic operates but I suppose it does, otherwise all tests would be broken. And expecting a list of strings for a single field (99% case) would be a pity.

there's actually no special-casing here because

def validates(*field_names: str)

allows one or more field names to be passed positionally.

@lafrech

lafrech commented Jan 27, 2025

Copy link
Copy Markdown
Member

But then in validates, the code shall not iterate on the string if a single field name is passed. If we blindly iterate on field_names when a single string is passed, it won't work. I expected to see a type check such as iterable_not_string.

(I'm not talking about mypy / type checking but about runtime.)

@sloria

sloria commented Jan 27, 2025

Copy link
Copy Markdown
Member

field_names will always be a tuple, even when a single argument is passed.

>>> def validates(*field_names: str):
...     print(type(field_names))
...     
>>> validates("foo")
<class 'tuple'>

@lafrech

lafrech commented Jan 27, 2025

Copy link
Copy Markdown
Member

Oh sorry, I missed the obvious.

The user never passes a tuple or list, just N positional args.

@lafrech

lafrech commented Mar 3, 2025

Copy link
Copy Markdown
Member

I just rebased this but couldn't figure out how to (force) push into the original PR branch.

Here's the rebase: https://github.com/marshmallow-code/marshmallow/compare/pr/1965

Is there anything else that needs to be done to merge this?

@sloria

sloria commented Mar 4, 2025

Copy link
Copy Markdown
Member

no blockers to merging!

@sloria
sloria enabled auto-merge (squash) March 4, 2025 05:03
@sloria
sloria merged commit 2fc8207 into marshmallow-code:dev Mar 4, 2025
@lafrech

lafrech commented Mar 5, 2025

Copy link
Copy Markdown
Member

Looks like we're done with milestone 4.0.

Should we ship? Review open issues/PR to see if anything else should be crammed in before releasing?

#2799 and #2810 are not breaking so they could be added later.

@sloria

sloria commented Mar 6, 2025

Copy link
Copy Markdown
Member

nice! let's let dev bake for a few weeks. i'm traveling a bit for the next couple weeks. once i'm back home i'll review issues/PRs that could be breaking-but-low-impact that could go into v4

@lafrech

lafrech commented Mar 6, 2025 via email

Copy link
Copy Markdown
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants