Skip to content
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

Custom error messages #174

Closed
luizpericolo opened this issue Nov 23, 2015 · 31 comments
Closed

Custom error messages #174

luizpericolo opened this issue Nov 23, 2015 · 31 comments

Comments

@luizpericolo
Copy link

Could not find anywhere in the docs how I can define custom validation error messages, so I'm assuming that this is not implemented. Please link me to the doc page if I'm mistaken.

I think this is a really good functionality, specially if you want to use cerberus to validate request structure and don't want to process the outcome of the errors dict any further and wish to create beautiful error messages for your response.

@funkyfuture
Copy link
Member

do you want custom messages for contributed or for custom rules?
am i understanding correctly that you want a string as result?

@funkyfuture
Copy link
Member

oh, and are humans the intended audience?

@luizpericolo
Copy link
Author

Yes, I want strings and humans are the intended audience.

If I get a error validating a string field according to a regex rule I get the following error:

{
    "name": "value does not match regex '^[a-zA-Z0-9]$'"
}

I'd like to be able to assign a custom message in case the field 'name' is not valid. Something along the lines of:

{
    "name": "value must only contain letters and digits"
}

@funkyfuture
Copy link
Member

a HumanReadableErrorHandler you could subclass and override error messages is on my agenda (#93).
but i want to implement XML, JSON and YAML-output next; slowly but steadily evolving the underlying mechanics.
unfortunately i can't give you a reliable estimation atm. it all might happen this year or at the beginning of the next. but several other projects need my attention.

you have an interesting example here as the error message is depending on the full constraint, not only the rule.

@andreymal
Copy link

+1

Also it is required for non-english projects, like this:

{
    "name": lazy_gettext("It seems that the user name is too complicated! Maybe you try something simpler?")
}
# => "Кажется, имя пользователя слишком сложное! Может, попробуете что-нибудь попроще?"

@andreymal
Copy link

I found a temporary workaround for 0.10:

import cerberus


class CustomErrorHandler(cerberus.errors.BasicErrorHandler):
    def __init__(self, tree=None, custom_messages=None):
        super(CustomErrorHandler, self).__init__(tree)
        self.custom_messages = custom_messages or {}

    def format_message(self, field, error):
        tmp = self.custom_messages
        for x in error.schema_path:
            try:
                tmp = tmp[x]
            except KeyError:
                return super(CustomErrorHandler, self).format_message(field, error)
        if isinstance(tmp, dict):  # if "unknown field"
            return super(CustomErrorHandler, self).format_message(field, error)
        else:
            return tmp


v = cerberus.Validator(
    {'username': {'minlength': 2, 'regex': r'[A-z0-9]+'}},
    error_handler=CustomErrorHandler(custom_messages={'username': {'regex': u'Не мудрите'}})
)
print(v.validate({'username': '⌀'}))  # => False
print(v.errors)  # => {'username': ['min length is 2', 'Не мудрите']}

(actually 'max length is 2' because #209 :)

@Pithikos
Copy link

+1 this seems like a very practical feature!

@funkyfuture
Copy link
Member

@Pithikos fyi: https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments

@ghost
Copy link

ghost commented Aug 29, 2016

I was looking for i10n too but it seems it's not on docs, it would be great to add them.

@funkyfuture I feel like your work in #93 is hidden under code, add it to docs and make it shine :)

@funkyfuture
Copy link
Member

funkyfuture commented Aug 29, 2016

to clarify:

  • a HumanReadableErrorHandler is not about the localization of the errors.BasicErrorHandler, but about an errorhandler that returns a list of prose in strings, including l10n
  • that error handler would live here
    • there's only a reference xml-error handler in that collection on my disk yet
    • i didn't plan to release anything of the collections before the 1.0 of cerberus

@k1-hedayati would you be interested in contributing? having a non-latin-alphabet in the mix from the beginning would certainly be useful. is Farsi a rtl-language?

@ghost
Copy link

ghost commented Aug 30, 2016

I didn't notice about 1.0, I thought your code is already on stable branch.

@funkyfuture yes I will be happy to contribute on this, how can I help?
Yes Farsi and Arabic both are rtl

@funkyfuture
Copy link
Member

@k1-hedayati i've updated the mentioned repo, so one should get an idea what the whole thing is about. feel free to open a related issue there. any contribution regarding design proposals, implementation, tests or translations (workflows) is welcome.

@leebenson
Copy link

Are custom error messages implemented yet?

This would be a great feature.

I have a multi-lingual app, and want to show messages such as 'Please enter your e-mail' or 'S'il vous plaît entrer votre e-mail' instead of a static 'required field'.

Thanks for your hard work on this library.

@funkyfuture
Copy link
Member

malheureusement, non. i just can point you at this issue: funkyfuture/cerberus-collections#2 (just updated some bits). i have no capacities to join in actively, but certainly will manage to review and release.

more complex error handlers will not find the way into cerberus in order to not depend on these in the release cycle.

@andreymal andreymal mentioned this issue Dec 9, 2017
@funkyfuture
Copy link
Member

funkyfuture commented Dec 9, 2017

@andreymal, you don't necessarily need to override the format_message method: https://stackoverflow.com/q/47730454/2489914

cc @Arion-Dsh @nykolaslima

@andreymal
Copy link

andreymal commented Dec 9, 2017

@funkyfuture what if I wish to set different messages for different data types? For example, "Username is invalid" for username field and "Email is invalid" for email field. In the same validator of course

@funkyfuture
Copy link
Member

yeah, then you have to tinker around. i related my comment to the i18n part that came up.

@andreymal
Copy link

@funkyfuture actually I combine both methods in production code :) https://pastebin.com/fkhZvBGr

@pimtel
Copy link

pimtel commented Dec 12, 2017

Any plan to implement internationalization?

I think it could be a good feature. Same as @leebenson, I want to show error messages in different languages.

@pimtel
Copy link

pimtel commented Dec 12, 2017

I thought localization could be implemented like jQuery validation as follow:
https://github.com/jquery-validation/jquery-validation/tree/master/src/localization

Instead of have multiple files, cerberus could have a module called localization.py and we could set as follow:

v = Validator(schema, lang="pt-BR")

Does anyone agree?

@andreymal
Copy link

@rgercp imho, simple and universal lazy_gettext is a better solution

@rismanrp
Copy link

rismanrp commented May 18, 2018

If you don't want to mess with the error handler and your app doesn't need language features anyway, you can make a custom regex validator with the error message as a part of the argument for your custom validator

from cerberus import Validator
import re

class MyCustomValidator(Validator):
	def _validate_lazyregex(self, lazyregex_options, field, value):
		""" {'type': 'dict'} """
		pattern = lazyregex_options['pattern']
		fail_message = lazyregex_options['fail_message']
                if not isinstance(value, _str_type):
			return
		if not pattern.endswith('$'):
			pattern += '$'
		re_obj = re.compile(pattern)
		if not re_obj.match(value):
			self._error(field, fail_message)
schema = {
       'email': {
		'type': 'string',
		'required': True,
		'lazyregex': {
			'pattern': '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$',
			'fail_message': 'Doesn\'t look like an email to me, man'
		}
	}
}

This is not the recommended approach, but just another option for the people who stumble upon this problem in the future.

@sagarkaurav
Copy link

sagarkaurav commented Feb 28, 2020

@nicolaiarocci @andreymal
unable to find documentation for the custom error messages.

I tried the slightly modified version of the solution given below and it works with simple documents it fails when using complex docs.
stackoverflow
Modified version

class CustomErrorHandler(BasicErrorHandler):
    def __init__(self, schema):
        self.custom_defined_schema = schema

    def _format_message(self, field, error):
        error_msg = self.custom_defined_schema[field].get('meta', {}).get('_errors', {}).get(error.rule)
        if not error_msg:
           return error_msg
        return super(CustomErrorHandler, self)._format_message(field, error)

It feels like a workaround and also that's a lot of work just to give an alternate message.

@Abhisek1994Roy
Copy link

Abhisek1994Roy commented Apr 26, 2020

@funkyfuture I am trying to keep the custom error messages for certain fields in the meta tag.
However, I am not able to access this meta tag for inner elements. Example, if a key is in a nested dictionary, then the below line of code does not allow me to access the meta tag. Can you suggest me a better way to handle this?
error_msg = self.custom_defined_schema[field].get('meta', {}).get('error_message', field)

My CustomErorHandler has been attached below. I am trying to get meta tags for specific types of errors and throw them.

class CustomErrorHandler(BasicErrorHandler):
    def __init__(self, schema=None, tree=None):
        super().__init__()
        self.custom_defined_schema = schema
        if tree is not None:
            self.tree = tree
    def _format_message(self, field, error):
        logger.error("Field- " + str(field) + " | " + "Value- " + str(error.value) + " | " + "Error- " + str(
            super(CustomErrorHandler, self)._format_message(field, error)))
        print(error.value, "---", error.code)

        handle_error_fields = [0, 2, 36, 37, 66, 68]
        if error.code == 3:
            return "Field passed -" + field + " is not present in master. Please contact admin!"
        if error.code in handle_error_fields:
            return super(CustomErrorHandler, self)._format_message(field, error)

        error_message_found = False
        try:
            error_msg = self.custom_defined_schema[field].get('meta', {}).get('error_message', field)
            error_message_found = True
        except KeyError:
            pass
        if error_message_found == False:
            error_msg = self.custom_defined_schema[field].get('meta', {}).get('error_message', field)


        if error_msg == field:
            return super(CustomErrorHandler, self)._format_message(field, error)
        return error_msg

@funkyfuture
Copy link
Member

Can you suggest me a better way to handle this?

not really from a quick glance and no knowledge of the use-case (and i'm not interested in solving it), but it could eventually be easier to not base on the BasicErrorHandler. instead implement a custom error handler that suits your exact requirements.

error_msg = self.custom_defined_schema[field].get('meta', {}).get('error_message', field)

should that secong call to get return the field name as default?

@Abhisek1994Roy
Copy link

Abhisek1994Roy commented Apr 27, 2020

Have figured it out @funkyfuture . Thanks. Had to use the schema_path to get the meta field

@sagarkaurav
Copy link

@Abhisek1994Roy can you give some example how you did it

@pbrus
Copy link

pbrus commented Oct 7, 2020

I had the same problem. I prepared a tool to customize error messages. If you need, please don't hesitate to use Cerberror.

@funkyfuture
Copy link
Member

@pbrus , out of curiosity, why don't you use the ErrorHandler API?

@pbrus
Copy link

pbrus commented Oct 8, 2020

@funkyfuture, because I don't need it and it seems to be somehow limited. Let me explain this.

If I understood your post correctly, inheriting from BasicErrorHandler allows to define a new error message1. Cerberror doesn't need these messages. The translate method matches defined messages with paths2. This is happening outside the validator of Cerberus. There is no sense to put these messages into errors.BasicErrorHandler.messages container using API and get them back in the next step to, finally, assign them to the paths. This is the first reason why I omit ErrorHandler API.

The second reason is caused by the limitation of API. Using it we are able to define new messages globally. Let's say that we need to define two fields with regex rules (code 65). The first one is a name of a user which must start with an upper-case letter. The second field is to store an email address. Using API we can set one error message for these fields. For me the better approach is:

('user', 'name') 65 "The name {{value}} must start with upper-case letter"
('user', 'email') 65 "The address email must contain @ sign"

This is what @ha1zum achieved partially. But this still doesn't solve a problem with unclear paths.

If we consider single fields we can easy handle them, modify them and so on. But the problems occur when we play with many nested fields. The definition of a schema is growing fast and I think that playing with a code is harder than just defining a file with paths and messages. And that is why I created Cerberror.


1 IMHO such code example should be in the Errors section.
2 I prefer to name the method translate instead of match because as an interface the first one should be more intuitive describing what the method does.

@funkyfuture
Copy link
Member

afaict this issue turned into a mix of support and feature request. stackoverflow may be used to find solutions with the given means of production. furthermore we don't intend API extension / a new major rlease, so i'm closing tis issue.

@funkyfuture funkyfuture closed this as not planned Won't fix, can't repro, duplicate, stale Jul 23, 2023
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

No branches or pull requests

10 participants