Ir para o conteúdo

Validators

Data validation system.

Validator Types

Type Base Class Usage
Sync Validator Format validation
Async AsyncValidator Database validation

Database Validators (Async)

UniqueValidator

from strider.validators import UniqueValidator

validator = UniqueValidator(
    model=User,
    field_name="email",
    message="This email already exists.",
    exclude_pk=None,  # Exclude on update
)

# Usage
await validator(value, session)

UniqueTogetherValidator

from strider.validators import UniqueTogetherValidator

validator = UniqueTogetherValidator(
    model=Post,
    fields=["slug", "workspace_id"],
    message="Slug must be unique per workspace.",
)

# Usage (pass dict)
await validator({"slug": "test", "workspace_id": 1}, session)

ExistsValidator

from strider.validators import ExistsValidator

validator = ExistsValidator(
    model=Category,
    field_name="id",
    message="Category does not exist.",
)

# Usage
await validator(category_id, session)

Format Validators (Sync)

RegexValidator

from strider.validators import RegexValidator

validator = RegexValidator(
    pattern=r"^[A-Z]{3}-\d{4}$",
    message="Invalid code format. Use XXX-0000.",
)

validator("ABC-1234")  # OK
validator("abc-1234")  # Raises ValidationError

EmailValidator

from strider.validators import EmailValidator

validator = EmailValidator()
validator("user@example.com")  # OK

URLValidator

from strider.validators import URLValidator

validator = URLValidator(schemes=["http", "https"])
validator("https://example.com")  # OK

SlugValidator

from strider.validators import SlugValidator

validator = SlugValidator(allow_unicode=False)
validator("my-slug-123")  # OK

Brazilian Validators

PhoneValidator

from strider.validators import PhoneValidator

validator = PhoneValidator()
validator("11999998888")  # OK (10 or 11 digits)

CPFValidator

from strider.validators import CPFValidator

validator = CPFValidator()
validator("12345678909")  # Validates check digits

CNPJValidator

from strider.validators import CNPJValidator

validator = CNPJValidator()
validator("11222333000181")  # Validates check digits

Range Validators

MinLengthValidator / MaxLengthValidator

from strider.validators import MinLengthValidator, MaxLengthValidator

min_validator = MinLengthValidator(min_length=3)
max_validator = MaxLengthValidator(max_length=100)

min_validator("ab")  # Raises: min 3 characters
max_validator("x" * 101)  # Raises: max 100 characters

MinValueValidator / MaxValueValidator

from strider.validators import MinValueValidator, MaxValueValidator

min_validator = MinValueValidator(min_value=0)
max_validator = MaxValueValidator(max_value=100)

min_validator(-1)  # Raises: min value is 0
max_validator(101)  # Raises: max value is 100

RangeValidator

from strider.validators import RangeValidator

validator = RangeValidator(min_value=1, max_value=10)
validator(5)  # OK
validator(11)  # Raises: must be between 1 and 10

DecimalPlacesValidator

from strider.validators import DecimalPlacesValidator

validator = DecimalPlacesValidator(max_digits=10, decimal_places=2)
validator(123.45)  # OK
validator(123.456)  # Raises: max 2 decimal places

Choice Validators

ChoiceValidator

from strider.validators import ChoiceValidator

validator = ChoiceValidator(choices=["draft", "published", "archived"])
validator("draft")  # OK
validator("invalid")  # Raises: not a valid choice

ProhibitedValidator

from strider.validators import ProhibitedValidator

validator = ProhibitedValidator(prohibited=["admin", "root", "system"])
validator("user")  # OK
validator("admin")  # Raises: prohibited value

File Validators

FileExtensionValidator

from strider.validators import FileExtensionValidator

validator = FileExtensionValidator(allowed_extensions=["jpg", "png", "gif"])
validator("image.jpg")  # OK
validator("file.exe")  # Raises: extension not allowed

FileSizeValidator

from strider.validators import FileSizeValidator

validator = FileSizeValidator(max_size=5 * 1024 * 1024)  # 5MB
validator(file_size_bytes)

Password Validator

from strider.validators import PasswordValidator

validator = PasswordValidator(
    min_length=8,
    max_length=128,
    require_uppercase=True,
    require_lowercase=True,
    require_digit=True,
    require_special=False,
)

validator("Password123")  # OK
validator("weak")  # Raises: doesn't meet requirements

Composite Validators

ComposeValidators (Sync)

from strider.validators import ComposeValidators, MinLengthValidator, MaxLengthValidator

validator = ComposeValidators([
    MinLengthValidator(3),
    MaxLengthValidator(50),
    SlugValidator(),
])

validator("my-slug")  # Runs all validators

ComposeAsyncValidators (Async)

from strider.validators import ComposeAsyncValidators, UniqueValidator, ExistsValidator

validator = ComposeAsyncValidators([
    UniqueValidator(User, "email"),
    ExistsValidator(Workspace, "id"),
])

await validator(data, session)

Usage in ViewSets

unique_fields

Auto-validates uniqueness.

class UserViewSet(ModelViewSet):
    model = User
    unique_fields = ["email", "username"]

field_validators

Custom validators per field.

class UserViewSet(ModelViewSet):
    model = User
    
    field_validators = {
        "email": [EmailValidator()],
        "phone": [PhoneValidator()],
    }

validate_{field} Method

Custom validation method.

class UserViewSet(ModelViewSet):
    model = User
    
    async def validate_email(self, value, db, instance=None):
        if value.endswith("@spam.com"):
            raise ValidationError("Email domain not allowed", field="email")
        return value

validate() Method

Cross-field validation.

class OrderViewSet(ModelViewSet):
    model = Order
    
    async def validate(self, data, db, instance=None):
        if data.get("quantity", 0) > data.get("stock", 0):
            raise ValidationError("Quantity exceeds stock")
        return data

Utility Functions

validate_all (Sync)

from strider.validators import validate_all, MinLengthValidator, MaxLengthValidator

errors = validate_all(
    value="ab",
    validators=[MinLengthValidator(3), MaxLengthValidator(50)],
)
# Returns list of ValidationError

validate_all_async (Async)

from strider.validators import validate_all_async

errors = await validate_all_async(
    value=email,
    validators=[UniqueValidator(User, "email")],
    session=db,
)

Custom Validator

Sync Validator

from strider.validators import Validator, ValidationError

class NoSpacesValidator(Validator):
    message = "Value cannot contain spaces."
    code = "no_spaces"
    
    def __call__(self, value):
        if " " in value:
            self.fail()
        return value

Async Validator

from strider.validators import AsyncValidator, ValidationError

class UniqueSlugValidator(AsyncValidator):
    message = "Slug already exists."
    code = "unique_slug"
    
    def __init__(self, model, exclude_pk=None):
        self.model = model
        self.exclude_pk = exclude_pk
    
    async def __call__(self, value, session, **context):
        query = self.model.objects.using(session).filter(slug=value)
        if self.exclude_pk:
            query = query.exclude(id=self.exclude_pk)
        if await query.exists():
            self.fail()
        return value

Error Format

{
  "detail": "Validation error",
  "code": "validation_error",
  "errors": [
    {
      "message": "This email already exists.",
      "code": "unique",
      "field": "email"
    }
  ]
}

Próximos passos