django-formset

repository·releases/2.2·Indexed 19 days ago

https://github.com/jrief/django-formset

A library to enhance Django forms and formsets with advanced CSS framework renderers (Bootstrap 5, Bulma, Foundation 6, Tailwind, UIkit), interactive web components, and asynchronous file uploads. It provides features such as nested form collections, conditional field visibility, autocomplete selects, and Ajax submission via the <django-formset> web component. The library also includes a declarative ModelAdmin for integration with Django Admin and a render_richtext template tag for JSON-to-HTML transformation.

Tokens
49.3K
Snippets
134
Records
179
Agent score
66%

What's inside django-formset

  1. Core features and user experience of django-formset

    releases/2.2

    The django-formset library is designed to improve the Django form user experience while maintaining an interface as close to standard Django forms, models, and views as possible.

    Key features include:

    • Pre-validation: Browser-side validation occurs before submission using the same constraints declared in your Python Django forms or models.
    • Ajax Submission: Forms are submitted via Ajax to prevent full page reloads.
    • Error Rendering: Server-side validation errors are returned to the browser and rendered next to the specific rejected field. Non-field errors are rendered with the form.
    • CSS Framework Support: Includes built-in renderers for:
      • Bootstrap 5
      • Bulma
      • Foundation 6
      • Tailwind (provides an opinionated set of CSS classes)
      • UIKit
    • Zero Dependencies: The client-side is written in pure TypeScript and compiles to a single, portable JS file with no external JavaScript dependencies required.
    • Advanced Widgets: Supports standard Django widgets, plus specialized ones for:
      • File uploads (handled asynchronously)
      • Searchable select boxes (server-side filtering)
      • Inlined radio buttons and checkboxes
      • Date/Datetime ranges
      • Phone numbers with country flags
      • Richtext editing
  2. Configure Form Collections with Siblings

    releases/2.2

    A FormCollection is considered a collection with siblings if it defines any of the following attributes:

    • min_siblings: The minimum number of collections required (defaults to 1).
    • max_siblings: The maximum number of collections allowed (no limit by default).
    • extra_siblings: The number of empty collections to start with (defaults to 0).

    Collections with siblings allow users to add or remove multiple instances of a collection. For each collection with siblings, there is one "Add" button, and each child collection has a "Remove" button (which is invisible by default and appears on hover).

    class MyCollection(FormCollection):
        min_siblings = 1
        max_siblings = 5
        extra_siblings = 2
        # ... fields ...
  3. Grouping and nesting forms in a formset

    releases/2.2

    A formset can group multiple forms into a collection, and these collections can be nested.

    Submission Behavior: When a formset is submitted, the data from the forms or collections is sent to the server as a group of separate entities.

    Dynamic Collections: Form-collections can be configured to have "list siblings." These can be dynamically adjusted in length using:

    • An "Add" button to increase the number of forms.
    • Multiple "Remove" buttons to decrease the number of forms.
  4. Best practices for submitting multiple forms

    releases/2.2

    While technically possible to submit multiple forms in a single HTTP request, it is generally discouraged because it can lead to data handling complexities on the server and conflicts with overlapping field names.

    Recommended approaches:

    1. Combine data from different forms into a single, unified form.
    2. Submit each form separately in its own request.
    3. Use JavaScript to collect data from multiple forms and combine them into a single request payload if they must be sent together.
  5. Conditional visibility for form fields and fieldsets

    releases/2.2
    You can hide or disable specific form fields or fieldsets based on logic. This is achieved by providing a Boolean expression as a condition, allowing for dynamic UI updates based on the state of other fields.
  6. Use conditional visibility and disabling in forms

    releases/2.2

    You can conditionally hide, show, or disable fields or fieldsets based on the current values of other fields in the formset. This is done by adding special attributes to the input fields or fieldsets. The condition argument accepts an expression that evaluates the current field values.

    Supported attributes:

    • df-show="condition"
    • df-hide="condition"
    • df-disable="condition"
  7. Use advanced widgets for Select and File fields

    releases/2.2

    The library provides enhanced widgets to improve UX:

    • Autocomplete Select: Replaces the standard <select> with an autocomplete version. It uses the same endpoint as the formset, so no extra URL routing is required.
    • Multi-Select Widgets: Offers two modes: one that keeps selected options inlined, and another that uses a 'source' and 'target' (dual-selector) pattern. The dual-selector supports database querying for large datasets, filtering, undo/redo, and optional sorting of selected items.
    • Asynchronous File Upload: Provides a drag-and-drop widget that uploads files asynchronously. This allows for file previews and faster form submissions since files are uploaded to a temporary server location before the final form submission.
  8. Compare DualSelector and SelectizeMultiple

    releases/2.2

    Choosing between DualSelector and SelectizeMultiple depends on the expected number of selections and available UI space:

    FeatureDualSelectorSelectizeMultiple
    Best Use CaseSelecting many options from a large listSelecting a few options (e.g., < 15)
    UI FootprintLarge (two side-by-side boxes)Compact (single input field)
    FunctionalityIncludes Undo/Redo and sortingSimple selection/removal
    ScalabilityHandles millions of entries via async loadingBetter for small, manageable sets

    Both widgets share the same lookup interface and can be swapped by changing the widget argument in the field or via the form's Meta class.

  9. Configure button actions for form submission

    releases/2.2

    The submission button in django-formset can hold a chain of actions. This allows you to define behaviors such as:

    • Disabling the button upon click.
    • Adding a spinning loading indicator during submission.
    • Specifying a success page via an HTML link directly in the button configuration, rather than hard-coding it in the Django view.
  10. Use `richtext-selection` to initialize dialog fields

    releases/2.2

    The richtext-selection attribute is used to map the editor's currently selected text to a dialog form field. This is typically used when a user selects text and then triggers an extension (like a hyperlink) to ensure the selected text is automatically populated as the initial value in the dialog (e.g., the 'Link Text' field).

    text = fields.CharField(
        label="Link Text",
        widget=widgets.TextInput(attrs={
            'richtext-selection': True,
            'size': 50,
        })
    )
  11. Use CollectionField to store structured data in a JSONField

    releases/2.2

    In django-formset 2.0+, you can use CollectionField to create deeply nested form collections and store their values as structured data in a single Django JSONField. This avoids the need to 'unroll' collections when converting between model data and forms.

    CollectionField takes all standard Django Field arguments plus an instance of a FormCollection. When rendered, the CollectionField is replaced by the forms and fields of that collection. It does not render a label by default, as the sub-fields provide their own labels.

    from formset.formfields.collection import CollectionField
    from formset.collection import FormCollection
    from django import forms
    
    class MySubForm(forms.Form):
        name = forms.CharField()
    
    class MyCollection(FormCollection):
        my_sub_form = MySubForm()
    
    class MyModelForm(forms.ModelForm):
        # This field will be serialized into the model's JSONField
        data = CollectionField(MyCollection)
    
        class Meta:
            model = MyModel
            fields = ['data']