How to Validate Data Before Scaling in Python
A reusable Python helper that validates required fields and constraint checks on data rows before entering a database pipeline, improving data quality and throughput.
Python code
44 linesdef validate_data(data, required_fields, constraints=None):
"""
Basic validation helper demonstrating data-quality workflows
before scaling (catches bad rows early, improves throughput).
"""
constraints = constraints or {}
errors = []
for field in required_fields:
if field not in data or data[field] == "" or data[field] is None:
errors.append(f"Missing required field: {field}")
for field, check in constraints.items():
if field in data and data[field] is not None and data[field] != "":
if not check(data[field]):
errors.append(f"Invalid value for {field}: {data[field]!r}")
return {"is_valid": not errors, "errors": errors}
def common_constraints():
"""Reusable validator functions for typical column types."""
return {
"age": lambda v: isinstance(v, int) and 0 <= v <= 150,
"email": lambda v: "@" in v and "." in v.split("@")[-1],
"score": lambda v: isinstance(v, (int, float)) and 0 <= v <= 100,
}
if __name__ == "__main__":
# Simulated incoming rows — one clean, one dirty
rows = [
{"name": "Alice", "age": 30, "email": "alice@example.com", "score": 85.5},
{"name": "", "age": 300, "email": "bob_at_bad_domain", "score": "high"},
]
required = ["name", "age", "email"]
checks = common_constraints()
# Fast pre-scaling validation loop
for i, row in enumerate(rows, 1):
result = validate_data(row, required, checks)
status = "PASS" if result["is_valid"] else "REJECT"
print(f"Row {i}: {status} -> {result['errors'] if result['errors'] else 'OK'}")
Output
Row 1: PASS -> OK
Row 2: REJECT -> ['Missing required field: name', 'Invalid value for age: 300', 'Invalid value for email: bob_at_bad_domain', 'Invalid value for score: high']
How it works
This helper runs validation before data hits your database, so bad rows are caught early—reducing writes and failures at scale. validate_data loops through required fields, emitting a missing-field error when the key is absent, empty, or None. For constraints, it applies provided check functions to present non-null values and records descriptive errors when checks fail. The result aggregates validity and errors into a single dict for easy use in batch or streaming pipelines. Using this pattern saves database I/O and avoids inserting invalid rows that later require cleanup or cause query anomalies.
Common mistakes
- Forgetting that an empty string is treated as missing, which may be valid for some optional fields.
- Assuming `constraints` values are callable; a non-callable check raises a `TypeError` during execution.
- Not applying constraints to empty or None values, which can skip required checks in some datasets.
Variations
- Use `pydantic` models for schema-based validation with type coercion and custom validators.
- Leverage `cerberus` or `jsonschema` for more complex nested validation rules.
Real-world use cases
- Cleaning records from CSV uploads before inserting into a production Postgres table, nipping malformed rows early.
- Pre-filtering user signup payloads in an API gateway to reject invalid age or email before hitting your user service.
- Validating event messages in a Kafka consumer before writing to a data warehouse, preventing corrupted analytics.
Sponsored
More from Database scaling & optimization
- Approximate Count with HyperLogLog in Python medium
- B-Tree Insert and In-Order Traversal in Python hard
- Broadcast a Small Reference Table in Python easy
- Build a Full Text Search Index in Python medium
- Build a Partial Index Mock in Python for Database Filtering easy
- Composite index leftmost prefix in Python medium
Keep learning
Related tutorials and quizzes for this topic.