strawberry
strawberry copied to clipboard
Field level data validators and pre/post processors
It would be very useful to have possibility to define field level validators especially for those fields which do not have resolvers. My proposal is to add validators parameter to field to strawberry.field and validator decorator. This idea came to my mind when I was designing API for strawberry-graphql-django package (https://github.com/strawberry-graphql/strawberry-graphql-django/issues/10). Soon we realized that this could be core feature of strawberry library.
I implemented prototype code and made draft pull request #809.
Validators could be used for following purposes
- field data validation
- data pre processing (input types)
- data post processing (output types)
Option 1
I'd propose following syntax for validators
def permission_validator(value, info):
if not info.context.request.user.has_permission():
raise Exception('Permission denied')
return value
@strawberry.type
class User:
name: str = strawberry.field(validators=[permission_validator])
Option 2
I'd also propose decorator syntax, which is quite similar with existing field resolver syntax. Strawberry field would provide validator decorator which could be then used to tie the field together with validator function.
@strawberry.input
class UserInput:
name: str = strawberry.field()
@name.validator
def name_validator(value, info):
if 'bad' in value:
raise ValueError('name contains bad word')
return value.title()
Option 3 (not doable)
def permission_validator(value, info):
if not info.context.request.user.has_permission():
raise Exception('Permission denied')
return value
@strawberry.type
class User:
# this does not work
@strawberry.validator(permission_validator)
name: str = strawberry.field()
I still think that maybe it's possible do add this functionality without first-classing support for validation, through other techniques such as user-defined function decorators (this gets a bit tricky with input types w/o resolvers, so maybe not?).
However, if we do want to first-class validation, I wonder if we should take a similar approach that Python's @property decorator does the getter and setter functions have the same symbol name:
####### Property
class SomeClass:
@property # implicitly my_field.getter
def my_field() -> bool:
return True
@my_field.setter
def my_field(value: bool):
print(f"Set my_field={value}"
####### Strawberry
@strawberry.type
class User:
@strawberry.field # implicitly name.resolver
def name() -> str:
return "First Last"
@name.validator
def name(value):
if 'bad' in value:
raise ValueError('name contains bad word')
return value.title()
Here, the same function symbol name is used for both the resolver and validator functions for the name field.
I think this might actually just be the standard practice in Python, and not actually a strict requirement with the property system; but it could be something for us to follow precedent
I added 3rd option. I think I personally would like implement both 2nd and 3rd. Any opinions?
Unfortunately, I don't think you can use decorators around assignments/expressions, only function/class definitions.
Unfortunately, I don't think you can use decorators around assignments/expressions, only function/class definitions.
~Decorator can add validator functions to the internal field definition structure. Generic field resolver can then iterate validators and execute them later. See: https://github.com/la4de/strawberry/commit/04b0ca2a0fe3e6b0a2dc65cd835846bc743ade67. I think this should be doable.~
You are right, it is not possible to do that. :)
don't know if this helps, but attrs has a concept of validators: https://www.attrs.org/en/stable/init.html#validators
don't know if this helps, but attrs has a concept of validators: https://www.attrs.org/en/stable/init.html#validators
I like this API. I think it would be a nice feature if Strawberry implemented it as well. (It's option 1 and 2 in the examples)
I implemented prototype code and made draft pull request #809.
It's really important feature. Any evolution ?
@stygmate I was not around during the original discussion, but I think this is already possible with field extensions.
Or is there any case where you think it would not work?
@bellini666 It’s more a quality of life problem for developers. It doesn’t seems possible to have declarations of validators as methods in input types by using extensions, does it ?
@stygmate would using Pydantic work for your use case?
@patrick91 I use a mix of strawberry-django and strawberry inputs. Sometimes needing to validate fields using subtypes not existing in django model. Can it work ?
@stygmate so you want to use django built-in validation? can you show me an example? 😊
@patrick91
not only.
a example (in code/pseudo-code 😅 ):
from django.db import models
class ExampleModel(models.Model):
name = models.CharField(max_length=255)
data = models.JSONField()
def __str__(self):
return self.name
import strawberry
import strawberry_django
@strawberry.input
class DataInput:
key: str
value: str
@strawberry_django.input(models...)
class ExampleModelInput:
name: str
data: list[DataInput]
here i transform the list of DataInput into a resolver but i want to be able to add validator directly into DataInput
something like that would be great:
@strawberry.input
class DataInput:
key: str
value: str
def validate_value(self, value):
if value .... some conditions:
raise ...
In reality i have more complex case with nested types and not only for transforming data to json.