Skip to main content

django-kpforms

A reusable Django application for dynamic form templates and submissions, powered by Lit-based web components.

  • Define form templates with sections, groups, and typed fields (numeric, string, text, radio, checkbox, datetime, calculated, ranged-radio)
  • Built-in tolerance checking for numeric and string fields
  • Interactive web component (<dynamic-form>) for end-user submission
  • Visual form-builder web component (<dynamic-form-builder>) for creating and editing templates
  • Printable-form component for generating PDFs via html2pdf.js
  • Full version history for form templates

Table of Contents

  1. Requirements
  2. Installation
  3. Quick Start
  4. Django Models & Helpers
  5. Web Components
  6. CSS Customisation Variables
  7. Contributing / Building from Source

Requirements

  • Python ≥ 3.14
  • Django ≥ 5.0
  • Pydantic ≥ 2.0

Installation

pip install django-kpforms

Or with uv:

uv add django-kpforms

Quick Start

1. Add to INSTALLED_APPS

# settings.py
INSTALLED_APPS = [
    ...
    "kpforms",
]

2. Run migrations

python manage.py migrate

3. Serve static files

django-kpforms ships with a compiled JavaScript bundle (kpforms.js) and KaTeX font files under kpforms/static/.

Make sure Django's static-file serving is configured and collectstatic has been run in production:

python manage.py collectstatic

4. Load the bundle in your template

{% load static %}

<!-- Load the kpforms web-component bundle -->
<script type="module" src="{% static 'kpforms.js' %}"></script>

<!-- Optional: load html2pdf for the printable-form component -->
<script src="{% static 'html2pdf.bundle.min.js' %}"></script>

Django Models & Helpers

Models

Model Description
FormTemplate A versioned form definition. The schema field holds a FormTemplateSchema JSON object.
FormTemplateVersionHistory Immutable snapshot of a FormTemplate at each version.
FormSubmission A single submission against a FormTemplate. Validates and stores tolerance results.

Helper functions (kpforms.helpers)

from kpforms.helpers import (
    create_form_template,
    update_form_template,
    submit_form_data,
    get_form_by_template_id_and_version,
    get_form_template_list,
    get_form_template_list_paginated,
    get_all_results_for_form_template,
)
Function Description
create_form_template(schema, user) Creates a new FormTemplate from a CreateFormTemplateSchema.
update_form_template(schema, user) Saves a new version of an existing template.
submit_form_data(template_id, form_data, user) Validates and saves a FormSubmission.
get_form_by_template_id_and_version(id, version=None) Returns the FormTemplateSchema for a given ID and optional version.
get_form_template_list(only_active=True) Returns a list of {id, name, description} dicts.
get_form_template_list_paginated(page_size, only_active) Returns a Django Paginator.
get_all_results_for_form_template(template_id) Returns all submissions with labelled field data.

Schema classes (kpforms.form_schema)

The core schema models are Pydantic models. The most important ones:

Class Description
FormTemplateSchema Full form definition: title, instruction, sections.
CreateFormTemplateSchema Input payload for creating a template.
UpdateFormTemplateSchema Input payload for updating a template (adds guid).
FormSubmissionSchema Submission payload: data (list of {fieldId, value}), fieldsOutOfTolerance.

Web Components

Include kpforms.js once on the page (see Quick Start above) — all components are self-registering and require no additional setup.

<dynamic-form>

Renders a form for end-user completion and submission.

<dynamic-form id="my-form"></dynamic-form>

<script type="module">
  const formEl = document.getElementById("my-form");

  // Pass the form schema (FormSchema JSON) as a property
  formEl.schema = {
    guid: "...",
    title: "My Form",
    sections: [...],
    generatePdfOption: false,
    generatePdfString: "Generate PDF",
    submitFormString: "Submit",
    submittingString: "Submitting…",
    apiConfig: {
      url: "/api/forms/submit/",
      method: "POST",
      successMessage: "Form submitted!",
      errorMessage: "Submission failed."
    }
  };

  // Optional: add extra keys to the submit POST body.
  // Must be a JSON-formatted string representing an object.
  formEl.extraPostParams = JSON.stringify({
    project_id: "abc123",
    station: "line-7"
  });
</script>

Properties / attributes:

Property Type Description
schema FormSchema (object) The full form schema to render.
lang string BCP-47 language tag for UI strings (e.g. "en", "sv"). Defaults to "en".
extraPostParams string (JSON object) Optional key/value pairs merged into the POST payload when submitting form results.
compact boolean Dense layout: smaller type/spacing, inline label column + value column. Default false. UI-only, never saved in the schema; also applies in view-only mode. See Compact mode.

<dynamic-form-builder>

An interactive editor that lets users create and edit form templates.

<dynamic-form-builder id="builder"></dynamic-form-builder>

<script type="module">
  const builder = document.getElementById("builder");

  // Optional: load an existing template for editing
  builder.schema = { guid: "...", name: "...", formSchema: { ... } };

  // Optional: add extra keys to the save-schema POST body.
  // Must be a JSON-formatted string representing an object.
  builder.extraPostParams = JSON.stringify({
    project_id: "abc123",
    edited_by: "frontend"
  });

  // Listen for save events
  builder.addEventListener("form-save", (e) => {
    const payload = e.detail; // CreateFormTemplateSchema | UpdateFormTemplateSchema
    fetch("/api/forms/templates/", {
      method: payload.guid ? "PUT" : "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
    });
  });
</script>

Properties / attributes:

Property Type Description
schema UpdateFormTemplateSchema (object) Optional existing template to edit.
lang string BCP-47 language tag.
extraPostParams string (JSON object) Optional key/value pairs merged into the POST payload when saving the schema.
compact boolean Dense editor (tight stack, labels above, no inline-label columns). Default false. See Compact mode.
foldableGroups boolean Each input group gets a fold bar (title, id, field count). UI-only state, never saved. Default false.

The builder's preview dialog has a Compact switch that renders the preview <dynamic-form> with compact. It is independent of the builder's own compact.

Events emitted:

Event detail Description
form-save CreateFormTemplateSchema | UpdateFormTemplateSchema Fired when the user clicks Save.

<printable-form>

Renders a read-only printable view of a submitted form that can be exported as a PDF using html2pdf.js.

<printable-form id="print-form"></printable-form>

<script type="module">
  const el = document.getElementById("print-form");
  el.schema   = { /* FormTemplateSchema */ };
  el.submission = { /* FormSubmission data */ };
</script>

Properties:

Property Type Description
schema FormTemplateSchema (object) The form template.
submission FormSubmissionSchema (object) The submission data to display.

CSS Customisation Variables

All web components use CSS custom properties (variables) to control their appearance. Set these on the element itself or any ancestor to theme the components.

The public API variables are prefixed --df-*. Internal variables map them to --form-* and to Shoelace design tokens.

Typography

Variable Default Description
--df-font-size-base 1rem Base font size.
--df-font-size-h1 1.8rem Form title size.
--df-font-size-h2 1.4rem Section heading size.
--df-font-size-label 0.9rem Field label size.
--df-font-size-description 0.85rem Field description / helper text size.

Colours

Variable Default Description
--df-color-text #333 Primary text colour.
--df-color-input-text #111 Text colour inside input fields.
--df-color-text-secondary #555 Secondary / description text colour.
--df-color-text-label #111 Field label text colour.
--df-color-background #ffffff Component background.
--df-color-background-input-base #ffffff Input field background (resting).
--df-color-background-input-focus #ffffff Input field background on focus.
--df-color-background-section #f9f9f9 Form section background.
--df-color-border #ccc Default border colour.

Accent Colour

Used for buttons, active tabs, focused borders, and Shoelace's primary palette.

Variable Default Description
--df-color-accent #007bff Primary accent / brand colour.
--df-color-accent-lighter #66b3ff Lighter variant (e.g. hover states).
--df-color-accent-darker #0056b3 Darker variant (e.g. active states).
--df-color-accent-text #ffffff Text on accent-coloured elements.
--df-color-success #28a745 Success colour (Shoelace success palette).
--df-color-success-darker #1e7e34 Darker success variant.
--df-color-success-text #ffffff Text on success-coloured elements.
--df-color-warning #fbb34e Warning colour.
--df-color-warning-darker #fea429 Darker warning variant.
--df-color-warning-text #ffffff Text on warning-coloured elements.
--df-color-neutral #6c757d Neutral colour.
--df-color-neutral-darker #5a6268 Darker neutral variant.
--df-color-neutral-text #ffffff Text on neutral-coloured elements.
--df-color-error #dc3545 Error / danger colour.
--df-color-error-lighter #fa5969 Lighter error variant.
--df-color-error-text #721c24 Text on error-coloured elements.

Status / Tolerance Colours

These control how fields are highlighted when their values are within or outside the defined tolerance range.

Variable Default Description
--df-color-required-star #f98989 Colour of the asterisk on required fields.
--df-color-within-tolerance-border #8fff8f Border colour when field value is within tolerance.
--df-color-within-tolerance-background #b9fb9f Background when field value is within tolerance.
--df-color-within-tolerance-input-text #214521 Text colour in input when within tolerance.
--df-color-within-tolerance-text #3c763d Label/message text when within tolerance.
--df-color-out-of-tolerance-border #fdf8b2 Border colour when field value is out of tolerance.
--df-color-out-of-tolerance-text #d9534f Text/label colour when out of tolerance.
--df-color-has-error-border (inherits error colour) Border colour when field has a validation error.
--df-color-has-error-background #d58d8b Background when field has a validation error.

Layout

Variable Default Description
--df-border-radius 4px Corner radius for inputs, sections, and the component itself.
--df-spacing-medium 1rem Medium spacing unit used for padding/gaps.
--df-spacing-large 1.5rem Large spacing unit used for section padding.
--df-button-padding 0.3rem 1rem Padding applied to Shoelace <sl-button> elements.

The tokens below are also public --df-* variables. Defaults shown are the regular (non-compact) values. Tokens marked -- in a compact column are not changed by compact mode for that component.

Variable Default Description
--df-input-padding 0.5rem 0.75rem Input padding.
--df-label-gap 0.25rem Space below field label.
--df-field-gap var(--df-spacing-medium) Space between fields.
--df-checkbox-size 1.5em Checkbox/radio size.
--df-option-gap 0.5rem Gap between checkbox/radio options.
--df-option-group-padding 0.5rem Padding around an option group.
--df-option-flow column flex-flow of option groups.
--df-actions-gap 1rem Gap between form action buttons.
--df-group-padding var(--df-spacing-medium) Input group padding.
--df-group-margin var(--df-spacing-large) Input group margin.
--df-group-gutter 1rem Builder: left indent of group content.
--df-option-box-padding 0.5rem Builder: option box padding.
--df-option-box-gap 0.5rem Builder: gap between option boxes.
--df-icon-button-size 3rem Add/remove option icon button size.
--df-remove-button-size 2rem Remove button size.
--df-move-button-size 1.75rem Builder: move field button size.
--df-tab-padding 0.5rem 1rem Tab padding.
--df-instruction-button-padding 0.5rem 1rem Instruction fold button padding.
--df-field-display flex Field display.
--df-field-columns none Field grid-template-columns (used when display is grid).
--df-field-value-column auto Grid column of the field value.
--df-field-column-gap 0 Gap between label and value columns.
--df-field-align normal Field align-items.

Compact mode

Set the compact attribute on <dynamic-form> or <dynamic-form-builder>:

<dynamic-form compact></dynamic-form>
<dynamic-form-builder compact foldableGroups></dynamic-form-builder>

Compact mode only supplies fallback values (via internal --form-compact-* variables). An explicit --df-* variable always wins over compact, so a consumer theme keeps working unchanged. Example: dynamic-form { --df-font-size-base: 1.1rem; } stays 1.1rem with compact, while other compact values still apply.

Compact values (blank = same as regular default):

Variable Regular <dynamic-form compact> <dynamic-form-builder compact>
--df-font-size-base 1rem 0.85rem 0.9rem
--df-font-size-label 0.9rem 0.8rem 0.8rem
--df-font-size-description 0.85rem 0.72rem 0.75rem
--df-font-size-h1 1.8rem 1.3rem 1.4rem
--df-font-size-h2 1.4rem 1rem 1.1rem
--df-spacing-medium 1rem 0.4rem 0.5rem
--df-spacing-large 1.5rem 0.5rem 0.6rem
--df-button-padding 0.3rem 1rem 0.1rem 0.5rem 0.15rem 0.6rem
--df-input-padding 0.5rem 0.75rem 0.2rem 0.4rem 0.2rem 0.4rem
--df-label-gap 0.25rem 0 0
--df-field-gap var(--df-spacing-medium) -- 0.3rem
--df-checkbox-size 1.5em 1.05em 1.05em
--df-option-gap 0.5rem 0.3rem 0.1rem
--df-option-group-padding 0.5rem 0.1rem --
--df-option-flow column row wrap --
--df-actions-gap 1rem 0.5rem 0.5rem
--df-group-padding var(--df-spacing-medium) -- 0.4rem
--df-group-margin var(--df-spacing-large) -- 0.4rem
--df-group-gutter 1rem -- 1.7rem
--df-option-box-padding 0.5rem -- 0.25rem
--df-option-box-gap 0.5rem -- 0.25rem
--df-icon-button-size 3rem -- 1.6rem
--df-remove-button-size 2rem -- 1.6rem
--df-move-button-size 1.75rem -- 1.2rem
--df-tab-padding 0.5rem 1rem 0.25rem 0.6rem 0.25rem 0.6rem
--df-instruction-button-padding 0.5rem 1rem 0.25rem 0.5rem --
--df-field-display flex grid --
--df-field-columns none minmax(7rem, 11rem) 1fr --
--df-field-value-column auto 2 --
--df-field-column-gap 0 0.5rem --
--df-field-align normal center --

Narrow forms fall back to labels above values.

Example: Dark Theme

dynamic-form {
  --df-color-background:            #1e1e2e;
  --df-color-background-section:    #2a2a3e;
  --df-color-background-input-base: #2d2d42;
  --df-color-text:                  #cdd6f4;
  --df-color-text-label:            #cdd6f4;
  --df-color-input-text:            #cdd6f4;
  --df-color-border:                #45475a;
  --df-color-accent:                #89b4fa;
  --df-color-accent-darker:         #74a0e8;
  --df-color-accent-text:           #1e1e2e;
}

Contributing / Building from Source

Prerequisites

  • Python ≥ 3.14 (with uv)
  • Node.js ≥ 20 with npm

Build

Use the included build.sh to produce a distributable wheel and sdist:

# Build both sdist and wheel (default)
./build.sh

# Build wheel only
./build.sh --wheel

# Build sdist only
./build.sh --sdist

The script will:

  1. Run npm run build inside kpforms/kpforms/ to compile the TypeScript web components and copy the output into kpforms/static/.
  2. Run python manage.py makemigrations kpforms to ensure migrations are up-to-date.
  3. Run uv build to produce the final packages in dist/.

Running Tests

uv run pytest

Licence

MIT — see LICENSE for details.

Metadata

Release files for django-kpforms 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for django-kpforms 1.0.0
File Interpreter ABI Platform
django_kpforms-1.0.0-py3-none-any.whl Python 3 none any Details

Release files / django_kpforms-1.0.0-py3-none-any.whl

Download URL django_kpforms-1.0.0-py3-none-any.whl
Size 2.2 MB
Tags Python 3
SHA-256 checksum
How to use checksums
7bbd87573db4e4685aabc89388d6fd469e24c13c8ab21920666c0c159c6e1407
BLAKE2b-256 checksum
How to use checksums
b404dee8c19dd30efc49a0b4a9bb7ce73f4be477c64f37e5781ddfe7ad3c69a1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.5

Security for django-kpforms 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page