28. Developing in django-formset

django-formset is a 3rd party Django library partially written in Python, TypeScript and PEGGY.js. The last two applications are required for the client part and make up about one third of the code base.

The code can be found on GitHub. Please use the issue tracker only to report bugs. For questions and new ideas, please use the discussion board.

When building this library locally, it therefore is strongly recommended that you install the whole tool-chain required to build the test application:

git clone https://github.com/jrief/django-formset.git
cd django-formset
python -m venv .venv
source .venv/bin/activate
pip install Django
pip install -r testapp/requirements.txt
pip install https://github.com/jrief/django-sphinx-view/archive/refs/heads/main.zip
pip install --no-deps -e .
npm install --include=dev
npm run tag-attributes
npm run function-code
npm run tailwindcss
npm run esbuild
npm run compilescss
npm run docscss
mkdir workdir
export DJANGO_DEBUG=true
sphinx-build -M json docs/source build
testapp/manage.py migrate
testapp/manage.py runserver

Open http://localhost:8000/ in your browser. This should show the same documentation you’re currently reading.

28.1. Setting up and running Tests

Since there is a lot of interaction between the browser and the server, the client is tested using pytest together with Playwright. The latter is a testing framework to run end-to-end tests using a headless browser. It must be initialized using:

playwright install

Then run the testsuite

pytest testapp

28.2. Building the Parsers

The content of the button attribute df-click, and the input field and fieldset attributes df-show, df-hide and df-disable are parsed before being evaluated by the code implementing the web component. This parser is generated using PEGGY and compiles to a pure TypeScript module through npm run tag-attributes. The grammar describing this proprietary syntax can be found in assets/tag-attributes.pegjs. The final parser is generated using npm run tag-attributes and written to client/django-formset/tag-attributes.ts. It then is imported by the code implementing the web component client/django-formset/DjangoFormset.ts.

Another parse is built to convert Tiptap extensions, so that they can be loaded dynamically. This parser is generated using npm run function-code and written to client/django-formset/function-code.ts. It then is imported by the code implementing the web component client/django-formset/RichtextArea.ts.

28.3. Building the Client

The client part consists of a few TypeScript modules which all are compiled and bundled to JavaScript using npm run esbuild. The default TypeScript compiler used in this project is esbuild, which currently is the fastest compiler of its kind.

The client can be built in three ways:

npm run esbuild

This creates a bundle of JavaScript modules. The main entry point is found in file formset/static/formset/django-formset.js. This file only contains the core functionality, ie. that one, required for web component <django-formset>. The JavaScript modules for all the other components, such as <select is="django-selectize">, <django-dual-selector>, <textarea is="django-richtext">, etc. are loaded upon demand.

This is the default setting.

npm run esbuild -- --monolith

This creates one single monolithic JavaScript module, named formset/static/formset/django-formset.js. In some circumstances this might be preferable over many splitted modules.

npm run esbuild -- --debug

This compiles TypeScript to JavaScript without minimizing plus additional source maps. This build target should be used during development of client side code.

npm run rollup

This works similar to esbuild. However instead of using the esbuild compiler it uses rollup + babel + terser.

I haven’t found any compelling reason why to use rollup instead of esbuild, since building the bundle takes much longer and the output sizes are comparable. For reasons of code hygiene, one sample of the unit tests is run using this setup.

28.4. Building the Documentation

Thanks to the django-sphinx-view project, the documentation of django-formset can be built using embedded functional forms. During development this is very helpful, because the examples now sit side-by-side with documentation describing them.

In order for this to work, please run

make -C docs json
npm run docscss
python manage.py runserver

The first command builds the documentation as a set of JSON and Python files. They then are loaded by the django-sphinx-view module.

The second command builds some specially formatted CSS files. They are required, so that Tailwind-, Bootstrap- and the Furo-themes do not interfere with each other.

The third command starts the Django application with integrated documentation.

28.5. Running the Django Test App

The unit tests and the application used to test the functionality, share a lot of code. In my opinion this is really important, because when writing code for end users, manual testing is mandatory. Therefore all unit tests provided with this application have been manually verified. Otherwise I could not guarantee a user experience which feels natural.