Model Validation
StateZero provides client-side validation that leverages your server-side serializers as the source of truth. This approach ensures consistency between frontend validation and backend data integrity.
Philosophy
We favor server-side validation because:
- Serializers are the source of truth - Your Django REST Framework serializers already define validation rules
- Consistency - Frontend and backend use the same validation logic
- Security - Server-side validation cannot be bypassed by malicious clients
- Maintainability - One place to define and update validation rules
Performance
Validation is fast because it performs no database operations. It only:
- Checks users basic permissions (not model instance specific permissions)
- Validates data structure using your existing serializers
- Returns validation results without side effects
API
javascript
const instance = new Model(data)
await instance.validate(validateType, partial)
Parameters
validateType
: 'create'
or 'update'
- Different validation rules may apply
- Permission checks vary by operation type
- Defaults to auto-detection based on whether instance has a primary key
partial
: boolean
false
- Validate all required fields (full validation)true
- Validate only provided fields (field-level validation)- Defaults to
false
How It Works
The validation process automatically:
- Serializes your data - Converts FileObjects to paths, formats dates, handles relationships
- Sends to validation endpoint - Uses your existing Django serializer validation
- Returns results - Throws errors for invalid data, returns
true
for valid data
javascript
// ✅ Handles all field types automatically
const formData = {
title: 'My Document',
due_date: new Date(),
attachment: fileObject, // FileObject instance
priority: 1
}
const todo = new Todo(formData)
await todo.validate('create', false) // Automatically serializes everything
Use Cases
Form Validation
javascript
const handleSubmit = async (formData) => {
try {
const tempTodo = new Todo(formData)
await tempTodo.validate('create')
// Validation passed, proceed with save
} catch (error) {
// Handle validation errors
}
}
Field-Level Validation
javascript
const validateField = async (fieldName, fieldValue) => {
try {
const tempTodo = new Todo({ [fieldName]: fieldValue })
await tempTodo.validate('create', true) // partial validation
// Field is valid
} catch (error) {
// Show field error
}
}
Pre-Save Validation
javascript
const todo = new Todo({
title: 'Important Task',
attachment: uploadedFile
})
// Validate before saving
await todo.validate() // Auto-detects create vs update
await todo.save() // Proceed with confidence
Error Handling
Validation throws standard StateZero exceptions:
ValidationError
javascript
try {
const todo = new Todo(formData)
await todo.validate('create')
} catch (error) {
if (error instanceof ValidationError) {
// Field-specific validation errors
console.log(error.detail) // { field: ['Error message'], ... }
// Example: { title: ['This field is required'], priority: ['Invalid choice'] }
}
}
PermissionDenied
javascript
try {
await todo.validate('create')
} catch (error) {
if (error instanceof PermissionDenied) {
// User doesn't have permission to create this model
console.log(error.message)
}
}
Common Patterns
Vue.js Debounced Field Validation
javascript
import { debounce } from 'lodash'
const debouncedValidate = debounce(async (fieldName, fieldValue) => {
try {
const tempTodo = new Todo({ [fieldName]: fieldValue })
await tempTodo.validate('create', true)
// Clear any existing field error
delete fieldErrors[fieldName]
} catch (error) {
if (error.detail?.[fieldName]) {
fieldErrors[fieldName] = error.detail[fieldName][0]
}
}
}, 200)
Global Form Validation
javascript
const validateForm = async (formData) => {
try {
const tempTodo = new Todo(formData)
await tempTodo.validate('create', false) // Full validation
return { valid: true }
} catch (error) {
return {
valid: false,
fieldErrors: error.detail || {},
generalError: error.message
}
}
}
Best Practices
- Create temporary instances - Don't worry about performance, they're lightweight
- Use partial validation for individual fields - Set
partial: true
for field-level checks - Handle both field and general errors - Check
error.detail
for field errors,error.message
for general issues - Validate before save - Catch issues early in your user interface
- Debounce field validation - Avoid excessive API calls during typing
Tips
- FileObjects are handled automatically - No need to manually convert to paths
- Dates are serialized properly - JavaScript Date objects work seamlessly
- Relationships are resolved - Related model instances are converted to primary keys
- Empty fields are normalized - Empty strings become
null
for optional fields as needed