Repository: pydantic/pydantic

Issue:
Throw descriptive exception for incorrectly defined validator fields.
### Discussed in https://github.com/samuelcolvin/pydantic/discussions/3129

<div type='discussions-op-text'>

<sup>Originally posted by **SunsetOrange** August 24, 2021</sup>
I had been trying to locate a bug in my code for an hour or so, which I eventually narrowed down to an incorrectly defined custom validator decorator. I had placed multiple field strings into the decorator call, inside a list rather than as multiple args.

My own mistake, but Pydantic had been throwing a very unhelpful exception stating that my model was not hashable.
The exception was pointing at the class definition line, not the validator decorator.

`Exception has occurred: TypeError unhashable type: 'list'`

This confused me greatly for a good while as I tried to find a possible location in the model and its super classes where a list was being used as a dictionary key. As this is a mistake that others could easily make (and I think I have made it in the past as well), it would be nice and helpful to output an exception that identifies the mistake and provides helpful feedback. Else wise, the source of the exception is difficult to identify.

I proposing that the following code be added to class_validators.py at line 75.
```
    elif not all([isinstance(field, str) for field in fields]):
        raise ConfigError(
            "validator fields should be passed as separate string args. Do not pass multiple fields as a list, etc. "
            "E.g. usage should be `@validator('<field_name_1>', '<field_name_2>', ...)` "
            "NOT `@validator(['<field_name_1>', '<field_name_2>', ...], ...)`"
        )
```
</div>
Throw descriptive exception for incorrectly defined validator fields.
### Discussed in https://github.com/samuelcolvin/pydantic/discussions/3129

<div type='discussions-op-text'>

<sup>Originally posted by **SunsetOrange** August 24, 2021</sup>
I had been trying to locate a bug in my code for an hour or so, which I eventually narrowed down to an incorrectly defined custom validator decorator. I had placed multiple field strings into the decorator call, inside a list rather than as multiple args.

My own mistake, but Pydantic had been throwing a very unhelpful exception stating that my model was not hashable.
The exception was pointing at the class definition line, not the validator decorator.

`Exception has occurred: TypeError unhashable type: 'list'`

This confused me greatly for a good while as I tried to find a possible location in the model and its super classes where a list was being used as a dictionary key. As this is a mistake that others could easily make (and I think I have made it in the past as well), it would be nice and helpful to output an exception that identifies the mistake and provides helpful feedback. Else wise, the source of the exception is difficult to identify.

I proposing that the following code be added to class_validators.py at line 75.
```
    elif not all([isinstance(field, str) for field in fields]):
        raise ConfigError(
            "validator fields should be passed as separate string args. Do not pass multiple fields as a list, etc. "
            "E.g. usage should be `@validator('<field_name_1>', '<field_name_2>', ...)` "
            "NOT `@validator(['<field_name_1>', '<field_name_2>', ...], ...)`"
        )
```
</div>


Comments:

Patch:
diff --git a/pydantic/class_validators.py b/pydantic/class_validators.py
--- a/pydantic/class_validators.py
+++ b/pydantic/class_validators.py
@@ -75,6 +75,11 @@ def validator(
             "validators should be used with fields and keyword arguments, not bare. "  # noqa: Q000
             "E.g. usage should be `@validator('<field_name>', ...)`"
         )
+    elif not all(isinstance(field, str) for field in fields):
+        raise ConfigError(
+            "validator fields should be passed as separate string args. "  # noqa: Q000
+            "E.g. usage should be `@validator('<field_name_1>', '<field_name_2>', ...)`"
+        )
 
     if whole is not None:
         warnings.warn(


Test patch:
diff --git a/tests/test_validators.py b/tests/test_validators.py
--- a/tests/test_validators.py
+++ b/tests/test_validators.py
@@ -658,6 +658,22 @@ def check_a(cls, v):
     assert Model().a == 'default value'
 
 
+def test_validator_bad_fields_throws_configerror():
+    """
+    Attempts to create a validator with fields set as a list of strings,
+    rather than just multiple string args. Expects ConfigError to be raised.
+    """
+    with pytest.raises(ConfigError, match='validator fields should be passed as separate string args.'):
+
+        class Model(BaseModel):
+            a: str
+            b: str
+
+            @validator(['a', 'b'])
+            def check_fields(cls, v):
+                return v
+
+
 def test_datetime_validator():
     check_calls = 0