A Django-Ninja backend to specify FormKit schemas
Project description
Formkit-Ninja
A Django-Ninja framework for FormKit schemas and form submissions
Why
FormKit out of the box has awesome schema support - this lets us integrate FormKit instances as Django models
- Upload / edit / download basic FormKit schemas
- Translated "option" values from the Django admin
- Reorder "options" and schema nodes
- List and Fetch schemas for different form types
Use
To use, pip install formkit-ninja and add the following to settings INSTALLED_APPS:
INSTALLED_APPS = [
...
"formkit_ninja",
"ninja",
...
]
Quick Start for New Users
⭐ NEW - Create complete data collection apps with minimal coding!
# 1. Create a FormKit schema
./manage.py create_schema --label "Contact Form"
# 2. Bootstrap a complete Django app
./manage.py bootstrap_app --schema-label "Contact Form" --app-name contacts
# 3. Add to INSTALLED_APPS and migrate
# (Edit settings.py to add 'contacts')
./manage.py makemigrations && ./manage.py migrate
# 4. Start collecting data!
./manage.py runserver
See the Quick Start Guide for a complete walkthrough.
Code Generation
⭐ NEW in v0.8.1 - Database-driven code generation! Configure type mappings and field overrides through Django admin without writing Python code.
formkit-ninja can automatically generate Django models, Pydantic schemas, admin classes, and API endpoints from your FormKit schemas.
Database-Driven Configuration
Configure code generation rules through the Django admin:
# Django Admin → Code generation configs
formkit_type = "text"
node_name = "district"
django_type = "ForeignKey"
django_args = {"to": "pnds_data.zDistrict", "on_delete": "models.CASCADE"}
Generates:
# models.py
district = models.ForeignKey("pnds_data.zDistrict", on_delete=models.CASCADE)
Quick Start
Generate code from all schemas in your database:
./manage.py generate_code --app-name myapp --output-dir ./myapp/generated
Generate code for a specific schema:
./manage.py generate_code --app-name myapp --output-dir ./myapp/generated --schema-label "My Form"
Generated Files
The code generator creates the following files:
models.py- Django models for groups and repeatersschemas.py- Django Ninja output schemasschemas_in.py- Django Ninja input schemas (Pydantic BaseModel)admin.py- Django admin classesapi.py- Django Ninja API endpoints
Extensibility
formkit-ninja provides multiple extension points for customizing code generation:
- Database-Driven Config: Configure through Django admin (no code needed!) ⭐ NEW
- Custom Type Converters: Add support for custom FormKit node types
- Custom NodePath: Extend NodePath with project-specific logic
- Plugin System: Bundle multiple extensions together
- Custom Templates: Override Jinja2 templates for generated code
See the Database-Driven Code Generation guide for the new database configuration feature, or the Code Generation Guide for detailed documentation and examples.
API
Formkit-Ninja provides a REST API for managing FormKit schema nodes. The API requires authentication and specific permissions.
Authentication
All API endpoints require:
- Authentication: User must be logged in (session-based authentication)
- Permission: User must have the
formkit_ninja.change_formkitschemanodepermission
Unauthenticated requests receive 401 Unauthorized. Authenticated users without the required permission receive 403 Forbidden.
Endpoints
Create or Update Node
POST /api/formkit/create_or_update_node
Creates a new node or updates an existing one.
Request Body:
uuid(optional): UUID of node to update. If omitted, a new node is created.parent_id(optional): UUID of parent node (must be a group or repeater)$formkit: FormKit node type (e.g., "text", "group", "repeater")- Other FormKit node properties (label, name, etc.)
Response:
200 OK: Success, returnsNodeReturnTypewith node data400 Bad Request: Invalid input (e.g., invalid parent, deleted node)403 Forbidden: Insufficient permissions404 Not Found: Node with provided UUID does not exist (for updates)500 Internal Server Error: Server error
Update Behavior:
- When
uuidis provided, the node with that UUID is updated - If the node doesn't exist, returns
404 Not Found - If the node is inactive (deleted), returns
400 Bad Request - Parent-child relationships are automatically created/updated when
parent_idis provided
Delete Node
DELETE /api/formkit/delete/{node_id}
Soft deletes a node (sets is_active=False).
Response:
200 OK: Success, returnsNodeInactiveType403 Forbidden: Insufficient permissions404 Not Found: Node does not exist
Response Formats
All successful responses return consistent data structures:
-
NodeReturnType: For active nodes
key: UUID of the nodenode: FormKit node datalast_updated: Timestamp of last changeprotected: Whether the node is protected from deletion
-
NodeInactiveType: For deleted nodes
key: UUID of the nodeis_active:falselast_updated: Timestamp of last changeprotected: Whether the node is protected
-
FormKitErrors: For error responses
errors: List of error messagesfield_errors: Dictionary of field-specific errors
Validation
The API validates:
- Parent existence: If
parent_idis provided, the parent node must exist and be a group or repeater - Node existence: If
uuidis provided for updates, the node must exist and be active - FormKit type: The
$formkitfield must be a valid FormKit node type
Test
Pull the repo:
gh repo clone catalpainternational/formkit-ninja
cd formkit-ninja
uv sync
Database Setup
Tests and submission queryset annotations (e.g. with_unresolved_flags(), with_import_failure()) require PostgreSQL (pgtrigger, and JSONBAgg/JSONObject for annotations). Start a PostgreSQL container before running tests:
# Using Podman (recommended)
podman run --replace -d --name formkit-postgres -p 5435:5432 -e POSTGRES_HOST_AUTH_METHOD=trust docker.io/library/postgres:14-alpine
# OR using Docker
docker run -d --name formkit-postgres -p 5435:5432 -e POSTGRES_HOST_AUTH_METHOD=trust postgres:14-alpine
Then run tests:
uv run pytest
Playwright
Some tests require playwright. Install it with:
uv run playwright install
Note: For full development setup with real data, see DEVELOPMENT.md.
Lint
Format and lint code using ruff:
# Check formatting
uv run ruff format --check .
# Check linting
uv run ruff check .
For Contributors
Prerequisites
- Python 3.10-3.14
uvfor package management- Podman or Docker for PostgreSQL database
- Playwright (for browser-based tests)
Development Workflow
-
Set up the project:
uv sync uv run playwright install # Start PostgreSQL (see Database Setup above)
-
Run tests:
uv run pytest
-
Check code quality:
uv run ruff format --check . uv run ruff check . uv run mypy formkit_ninja
-
Test Driven Development (TDD):
- Write tests before implementing features
- Ensure new code is covered by tests
- Use
pytestas the testing framework
-
Code Style:
- Use
rufffor formatting and linting - Follow Python type hints for all function arguments and return values
- Adhere to SOLID principles
- Use
-
Commit Messages:
- Use Conventional Commits specification
- Format:
<type>(<scope>): <subject>
Updating 'Protected' Nodes
If a node's been protected you cannot change or delete it. To do so, you'll need to temporarily disable the trigger which is on it.
./manage.py pytrigger disable protect_node_deletes_and_updates
Make changes
./manage.py pgtrigger enable protect_node_deletes_and_updates
See the documentation for more details: https://django-pgtrigger.readthedocs.io/en/2.3.0/commands.html?highlight=disable
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file formkit_ninja-2.3.tar.gz.
File metadata
- Download URL: formkit_ninja-2.3.tar.gz
- Upload date:
- Size: 1.3 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Bluefin","version":"43","id":"Deinonychus","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e82cd75d0d6c2f0a1ccf55c7ace6981e0815b2e132d643db52fec82e2793c1ed
|
|
| MD5 |
17eefa0456767a8d380726b8aac00daf
|
|
| BLAKE2b-256 |
b181141cd11a5e3dbc62ad5544dc44e0da5e5ae08285de3bc1965a819739b70d
|
File details
Details for the file formkit_ninja-2.3-py3-none-any.whl.
File metadata
- Download URL: formkit_ninja-2.3-py3-none-any.whl
- Upload date:
- Size: 642.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Bluefin","version":"43","id":"Deinonychus","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff1d1ff093b05d92b5c3cd906684692bd8c43474df8726ee541dad19b540a685
|
|
| MD5 |
95dacfd7ece6c962a60722694f3a2af3
|
|
| BLAKE2b-256 |
24714b20b8329504fa6d8d88ea644971c68ab9b2479f964fa1b5e123aa090d8f
|