Skip to content

catalpainternational/formkit-ninja

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

254 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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 repeaters
  • schemas.py - Django Ninja output schemas
  • schemas_in.py - Django Ninja input schemas (Pydantic BaseModel)
  • admin.py - Django admin classes
  • api.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_formkitschemanode permission

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, returns NodeReturnType with node data
  • 400 Bad Request: Invalid input (e.g., invalid parent, deleted node)
  • 403 Forbidden: Insufficient permissions
  • 404 Not Found: Node with provided UUID does not exist (for updates)
  • 500 Internal Server Error: Server error

Update Behavior:

  • When uuid is 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_id is provided

Delete Node

DELETE /api/formkit/delete/{node_id}

Soft deletes a node (sets is_active=False).

Response:

  • 200 OK: Success, returns NodeInactiveType
  • 403 Forbidden: Insufficient permissions
  • 404 Not Found: Node does not exist

Response Formats

All successful responses return consistent data structures:

  • NodeReturnType: For active nodes

    • key: UUID of the node
    • node: FormKit node data
    • last_updated: Timestamp of last change
    • protected: Whether the node is protected from deletion
  • NodeInactiveType: For deleted nodes

    • key: UUID of the node
    • is_active: false
    • last_updated: Timestamp of last change
    • protected: Whether the node is protected
  • FormKitErrors: For error responses

    • errors: List of error messages
    • field_errors: Dictionary of field-specific errors

Validation

The API validates:

  • Parent existence: If parent_id is provided, the parent node must exist and be a group or repeater
  • Node existence: If uuid is provided for updates, the node must exist and be active
  • FormKit type: The $formkit field 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 require PostgreSQL due to the pgtrigger dependency. Start a PostgreSQL container before running tests:

# Using Podman (recommended)
podman run -d --name formkit-postgres -p 5433:5432 -e POSTGRES_HOST_AUTH_METHOD=trust docker.io/library/postgres:14-alpine

# OR using Docker
docker run -d --name formkit-postgres -p 5433: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
  • uv for package management
  • Podman or Docker for PostgreSQL database
  • Playwright (for browser-based tests)

Development Workflow

  1. Set up the project:

    uv sync
    uv run playwright install
    # Start PostgreSQL (see Database Setup above)
  2. Run tests:

    uv run pytest
  3. Check code quality:

    uv run ruff format --check .
    uv run ruff check .
    uv run mypy formkit_ninja
  4. Test Driven Development (TDD):

    • Write tests before implementing features
    • Ensure new code is covered by tests
    • Use pytest as the testing framework
  5. Code Style:

    • Use ruff for formatting and linting
    • Follow Python type hints for all function arguments and return values
    • Adhere to SOLID principles
  6. Commit Messages:

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

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •