build a form engine in python that processes form schemas, validates submissions, and handles schema migrations. you can optionally expose the engine through an API using django.
your engine should support three operations.
given a form schema (dict/JSON) and a submission (dict/JSON), validate the submission and return detailed, per-field errors.
the schema should support the following field types:
| type | description | accepted values | validation rules |
|---|---|---|---|
text |
single-line text input | string | required, min_length, max_length, pattern (regex) |
number |
numeric input | integer or float | required, min_value, max_value |
email |
email address | string in valid email format | required |
dropdown |
single selection from a list | string matching one of the defined options |
required, options (list of allowed values) |
checkbox |
true/false toggle | boolean | required |
date |
calendar date | string in YYYY-MM-DD format |
required, min_date, max_date |
repeating_section |
a group of fields the user can submit multiple times, like rows in a table | list of objects, each containing the nested fields | min_rows, max_rows |
calculated |
a field whose value is computed from other fields (e.g. "total = price * quantity") | not submitted by the user, computed by the engine | expression (a math expression referencing other field names) |
Note
the schema should also support conditional visibility: "show field B only if field A equals X". hidden fields should be excluded from validation.
Important
validation output should be structured and per-field. for repeating sections, errors should reference the specific row and field.
{
"id": "order-form-v1",
"title": "Product Order Form",
"fields": [
{
"name": "customer_email",
"type": "email",
"label": "Email Address",
"required": true
},
{
"name": "order_type",
"type": "dropdown",
"label": "Order Type",
"required": true,
"options": ["single", "bulk"]
},
{
"name": "bulk_quantity",
"type": "number",
"label": "Bulk Quantity",
"required": true,
"min_value": 10,
"visible_when": {
"field": "order_type",
"equals": "bulk"
}
},
{
"name": "items",
"type": "repeating_section",
"label": "Order Items",
"min_rows": 1,
"max_rows": 20,
"fields": [
{
"name": "product_name",
"type": "text",
"label": "Product",
"required": true
},
{
"name": "unit_price",
"type": "number",
"label": "Unit Price",
"required": true,
"min_value": 0
},
{
"name": "quantity",
"type": "number",
"label": "Quantity",
"required": true,
"min_value": 1
},
{
"name": "line_total",
"type": "calculated",
"label": "Line Total",
"expression": "unit_price * quantity"
}
]
}
]
}{
"customer_email": "john@example.com",
"order_type": "bulk",
"bulk_quantity": 50,
"items": [
{"product_name": "Widget A", "unit_price": 10.00, "quantity": 3},
{"product_name": "Widget B", "unit_price": 25.50, "quantity": 2}
]
}{
"valid": false,
"errors": {
"customer_email": ["invalid email address"],
"items": {
"0": {
"unit_price": ["value must be at least 0"]
},
"2": {
"product_name": ["this field is required"]
}
}
},
"computed_fields": {
"items": {
"0": {"line_total": -15.00},
"1": {"line_total": 51.00},
"2": {"line_total": null}
}
}
}if order_type is "single", bulk_quantity is hidden and should not be validated, even if it's marked as required:
submission:
{
"customer_email": "john@example.com",
"order_type": "single",
"items": [
{"product_name": "Widget A", "unit_price": 10.00, "quantity": 1}
]
}output:
{
"valid": true,
"errors": {},
"computed_fields": {
"items": {
"0": {"line_total": 10.00}
}
}
}given two versions of a form schema, produce a structured diff that describes what changed.
v1 is the schema above. v2 renames customer_email to email (indicated by a renamed_from property on the field), removes bulk_quantity, adds a new field shipping_address, and changes quantity's min_value from 1 to 5.
v2 schema (abbreviated, showing only what changed):
{
"id": "order-form-v2",
"title": "Product Order Form",
"fields": [
{
"name": "email",
"type": "email",
"label": "Email Address",
"required": true,
"renamed_from": "customer_email"
},
{
"name": "shipping_address",
"type": "text",
"label": "Shipping Address",
"required": true
}
]
}expected diff output:
{
"fields_added": [
{"name": "shipping_address", "type": "text"}
],
"fields_removed": [
{"name": "bulk_quantity", "type": "number"}
],
"fields_renamed": [
{"old_name": "customer_email", "new_name": "email"}
],
"fields_modified": [
{
"name": "items.quantity",
"changes": {
"min_value": {"old": 1, "new": 5}
}
}
]
}given a schema v1, a schema v2, and a list of submissions created under v1, migrate the submissions to conform to v2.
using the same v1 to v2 change above, a migration of the earlier valid submission would produce:
{
"migrated": [
{
"email": "john@example.com",
"order_type": "bulk",
"shipping_address": null,
"items": [
{"product_name": "Widget A", "unit_price": 10.00, "quantity": 3},
{"product_name": "Widget B", "unit_price": 25.50, "quantity": 2}
]
}
]
}Important
the migration should also return a report describing what actions were taken per submission. the format of the report is up to you.
your migration should handle at least these cases:
- added fields
- removed fields
- renamed fields
- type changes
- python 3.10+
- include tests using pytest
- include a README with setup instructions, how to run the tests, and your design decisions
- you can optionally expose the engine through a django API. if you do, include API documentation
- correctness
- edge case handling
- code quality
- testing
- documentation
- create a public github repository and share the link
- the repo should be runnable with a single command (
pytest,make test, or similar) - include any additional context you think is relevant