Skip to main content

django-form-matrix

Build status PyPI version PyPI - Python Version PyPI - Django Version Downloads

A Django form field to fill a table: one row per item, one column per property and, in every cell, a form field of its own — a checkbox, a number, a choice, text. Rows can be grouped in collapsible sections, and checkbox columns get shortcuts to check a row, a column or a whole section.

It works in any Django form and in the admin. A ready-made permission matrix (apps → models → add/view/change/delete) is included as a use case.

A matrix with checkboxes, numbers and choices in its cells, in two sections

Installation

pip install django-form-matrix

Add django_form_matrix to INSTALLED_APPS (it ships a template, a script and a stylesheet):

INSTALLED_APPS = [
    # ...
    "django_form_matrix",
]

Quick start

from django import forms
from django_form_matrix import Column, MatrixField, Row


class PlanForm(forms.Form):
    features = MatrixField(
        rows=[
            Row("invoices", "Invoices", section="Billing"),
            Row("quotes", "Quotes", section="Billing"),
            Row("exports", "Exports", section="Analytics", columns=["enabled"]),
        ],
        columns=[
            Column("enabled", "Enabled"),  # a checkbox unless said otherwise
            Column(
                "limit",
                "Monthly limit",
                forms.IntegerField(min_value=0, required=False),
            ),
        ],
    )

Render the form as usual and add {{ form.media }} (the admin does it for you). The cleaned value has one entry per row with the cells that exist:

form.cleaned_data["features"]
# {"invoices": {"enabled": True, "limit": 50},
#  "quotes": {"enabled": False, "limit": None},
#  "exports": {"enabled": False}}

That is what a JSONField stores, so a ModelForm can save it directly.

Rows and columns

  • Column(key, label, field=BooleanField(required=False)): the form field of the column. Its validation and its widget are Django's own, and a failing cell is reported with its row and column ("Invoices, Monthly limit: …").
  • Row(key, label, section=None, columns=None): section groups rows under a collapsible heading (rows without one go in a plain table); columns are the keys of the columns that have a cell in this row, the rest show a dash.
  • MatrixField(rows=…, columns=…) is not required by default; with required=True at least one cell needs a value. rows and columns can be reassigned in a form's __init__ to build the matrix dynamically.

Each cell is submitted as <name>__<row key>__<column key>, so it reads like any other widget.

Shortcuts

Checkbox columns get a checkbox in their heading, every row with checkboxes gets one at its start, and each section gets an "All". They show whether their cells are all, some or none checked, and the heading of a section counts the checked ones. A section opens by itself when something in it is filled.

Permissions

from django_form_matrix.contrib.permissions import PermissionMultipleChoiceField


class RoleAdmin(admin.ModelAdmin):
    def formfield_for_manytomany(self, db_field, request, **kwargs):
        if db_field.name == "permissions":
            kwargs["form_class"] = PermissionMultipleChoiceField
        return super().formfield_for_manytomany(db_field, request, **kwargs)

The permission matrix in the admin

One section per app, one row per model and a column per action; the other permissions of a model go in an "Others" column. It submits like a CheckboxSelectMultiple, so validation and saving do not change, and the names of the permissions are shown translated. Needs django.contrib.auth. docs/examples/permissions.md shows how to build the same matrix by hand with MatrixField, with rows that come from the database, for when you need more control.

Customizing the HTML

Both templates are split in blocks, so a template of yours can extend one and change only a piece, without copying the rest:

{% extends "django_form_matrix/matrix.html" %}
{% block section_summary %}<strong>{{ section.name }}</strong>{% endblock %}
{% block cell %}<div class="my-cell">{{ cell.html }}</div>{% endblock %}

Point a widget to it with template_name:

class MyMatrixWidget(MatrixWidget):
    template_name = "myapp/my_matrix.html"
Block matrix.html permissions.html What it is
section_summary ✓ ✓ The heading of a section (its name and count)
section_toggle ✓ ✓ The "All" checkbox of a section
column_heading ✓ ✓ The heading of a column
others_heading ✓ The heading of the "Others" column
row_heading ✓ ✓ The heading of a row
cell ✓ The widget of a cell ({{ cell.html }})
permission ✓ The checkbox of a permission
other_permission ✓ A permission of the "Others" column
missing_cell ✓ ✓ A cell a row does not have
empty ✓ ✓ What shows when there is nothing to fill

{{ block.super }} keeps the original of a block. The classes (.form-matrix-*) and the data-form-matrix-* attributes are what the stylesheet and the script look for: keep them in your blocks and the shortcuts keep working.

Django looks for the widget templates of a form in the templates/ of the installed apps, not in the DIRS of your project. To override django_form_matrix/matrix.html itself by name from the project's templates, set FORM_RENDERER = "django.forms.renderers.TemplatesSetting", or put the template in an app listed before django_form_matrix in INSTALLED_APPS. Extending it with your own template_name, as above, needs neither.

Styling

The stylesheet uses the colours of the Django admin when it is there. Anywhere else, set --form-matrix-border, --form-matrix-heading-bg and --form-matrix-quiet on .form-matrix.

Unfold

django-unfold does not define the variables of Django's admin, so add UnfoldMatrixMixin to the admin: it puts the matrix on Unfold's palette, light and dark, and styles the number, text and select cells like Unfold's own inputs.

from unfold.admin import ModelAdmin
from django_form_matrix.contrib.unfold import UnfoldMatrixMixin


@admin.register(Plan)
class PlanAdmin(UnfoldMatrixMixin, ModelAdmin):
    form = PlanForm

The matrix in Unfold

The permission matrix, the use case this library started from, in Unfold:

The permission matrix in Unfold

And in Unfold's dark mode:

The matrix in Unfold, dark

Translations

Ships a Spanish (es) translation: the matrix's headings ("Add", "View", "Change", "Delete", "Others"), the permissions' names ("Can view plan") and its messages follow the active language, like the rest of the admin.

Development

uv sync && npm install
uv run pytest            # Python + end-to-end (needs `uv run playwright install chromium`)
npm test                 # JS
cd example && uv run python manage.py runserver   # demo: Plans and Roles in the admin
DJANGO_SETTINGS_MODULE=example.settings_unfold uv run python manage.py runserver   # the same, in Unfold

Metadata

Release files for django-form-matrix 0.1.0

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

Source distribution (sdist)

Source distribution for django-form-matrix 0.1.0
File Size Uploaded Provenance Actions
django_form_matrix-0.1.0.tar.gz 370.6 kB With provenance Details for django_form_matrix-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for django-form-matrix 0.1.0
File Interpreter ABI Platform Actions
django_form_matrix-0.1.0-py3-none-any.whl Python 3 none any Details for django_form_matrix-0.1.0-py3-none-any.whl

Total release size: 390.3 kB

Release files / django_form_matrix-0.1.0.tar.gz

Download URL django_form_matrix-0.1.0.tar.gz
Size 370.6 kB
Tags Source
SHA-256 checksum
How to use checksums
684827ca4e31be638d1a2c668341126b6eb1dec0de4f58a703c4a8ee2a669289
BLAKE2b-256 checksum
How to use checksums
c41c89a4c3040ac3d38ce8a38e8fc5cb6bdcaac607bf32ae7b55ee4d7727f677
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release files / django_form_matrix-0.1.0-py3-none-any.whl

Download URL django_form_matrix-0.1.0-py3-none-any.whl
Size 19.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
78b14d61956d0ca8df69fceb0a5ca0b2f46aedbf183702b8140a7f159ee07cba
BLAKE2b-256 checksum
How to use checksums
b8970a111d3c2e7b286739cbe39472b1bec44c28750031df0f8be933874c2287
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Security for django-form-matrix 0.1.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.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for django_form_matrix-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

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

This release

0.1.0 This release

2 release files Full provenance

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