Skip to content

Latest commit

 

History

History
379 lines (227 loc) · 9.53 KB

fields-and-widgets.rst

File metadata and controls

379 lines (227 loc) · 9.53 KB

Fields and Widgets

Note

This document is for model fields and widgets. If you're looking for HTML form input fields, see: :doc:`User's guide on Input field types <guide/inputs>`.

Contents:

Model fields

.. module:: django_jsonform.models.fields
    :synopsis: Model fields


JSONField

.. versionadded:: 2.0

It is basically a subclass of Django's JSONField, but for convenience, it automatically sets up the JSON editor widget for you.

In Django < 3.1, for databases other than Postgres, it uses a TextField underneath.

Parameters:

.. attribute:: schema
    :type: dict, callable

    A ``dict`` or a callable object specifying the schema for the current field.

    A callable is useful for :ref:`specifying dynamic choices <dynamic choices>`.

    The callable function may optionally receive the current model instance. See:
    :ref:`Accessing model instance in callable schema`.

    .. versionchanged:: 2.1
        The ability to provide a callable was added.

    .. versionchanged:: 2.8
        Callable schema may receive an ``instance`` argument.

.. attribute:: pre_save_hook
    :type: callable

    .. versionadded:: 2.10

    (Optional) Sometimes you may wish to transform the JSON data before saving
    in the database.

    For that purpose, you can provide a callable through this argument which
    will be called before saving the field's value in the database.

    The ``pre_save_hook`` callable will receive the current value of the field
    as the only argument. It must return the value which you intend to save in
    the database.

    .. code-block::

        def pre_save_hook(value):
            # do something with the value ...
            return value


        class MyModel(...):
            items = JSONField(schema=..., pre_save_hook=pre_save_hook)

.. attribute:: file_handler
    :type: str

    .. versionadded:: 2.11

    (Optional) Provide a the url of the view for handling file uploads. See :ref:`document
    on uploading files <file url>` for usage.

.. attribute:: **options

    This ``JSONField`` accepts all the arguments accepted by Django's
    ``JSONField``, such as a custom ``encoder`` or ``decoder``.

    For details about other parameters, options and attributes of the
    ``JSONField``, see `Django's docs
    <https://docs.djangoproject.com/en/stable/ref/models/fields/#django.db.models.JSONField>`__.

Usage:

from django_jsonform.models.fields import JSONField


class MyModel(models.Model):
    ITEMS_SCHEMA = {...}

    items = JSONField(schema=ITEMS_SCHEMA)

ArrayField

.. versionadded:: 2.0

A subclass of Django's ArrayField except it renders a dynamic form widget.

It takes exactly the same arguments as the original class.

It also supports multiple levels of array nesting.

Usage:

from django_jsonform.models.fields import ArrayField


class MyModel(models.Model):
    items = ArrayField(models.CharField(max_length=50), size=10)
    # ...

For more details, see Django's docs.

Form fields

.. module:: django_jsonform.forms.fields
    :synopsis: Form fields

JSONFormField

.. versionadded:: 2.0

It is a subclass of Django's forms.JSONField.

Parameters:

.. attribute:: schema
    :type: dict, callable

    A ``dict`` or a callable object.

.. attribute:: model_name
    :type: string

    (Optional) Name of the model. It is sent along with the AJAX requests to your file handler
    view.

.. attribute:: file_handler
    :type: string

    (Optional) Provide a the url of the view for handling file uploads.

.. attribute:: **options

    It also accepts other options which are accepted by Django's ``forms.JSONField``.

    For details about other parameters, options and attributes, see `Django's docs
    <https://docs.djangoproject.com/en/4.1/ref/forms/fields/#jsonfield>`__.

Usage:

from django_jsonform.forms.fields import JSONFormField

class MyForm(forms.Form):
    my_field = JSONFormField(schema=schema)

Widgets

.. module:: django_jsonform.widgets
    :synopsis: Widgets


JSONFormWidget

The widget which renders the editor.

It can be used in a form if you don't want to use the model field.

Parameters:

.. attribute:: schema
    :type: dict, callable

    A ``dict`` or a callable object specifying the schema for the current field.

    A callable is useful for :ref:`specifying dynamic choices <dynamic choices>`.

    The callable function may optionally receive the current model instance.
    See: :ref:`Accessing model instance in callable schema`.

    .. versionchanged:: 2.1
        The ability to provide a callable was added.

    .. versionchanged:: 2.8
        Callable schema may receive an ``instance`` argument.

.. attribute:: model_name
    :type: str

    (Optional). The name of the model. It is passed to the file upload handler
    so that you can identify which model is requesting the file upload.

    See :ref:`Handling file uploads` for more details.

.. attribute:: file_handler
    :type: str

    .. versionadded:: 2.11

    (Optional) Provide a the url of the view for handling file uploads. See :ref:`document
    on uploading files <file url>` for usage.

.. attribute:: validate_on_submit
    :type: bool

    .. versionadded:: 2.12

    (Optional) Whether to validate the data on the browser when form is submitted.

    Default ``False``.

    See :ref:`Validating data in browser <validate-on-submit>` for more.

.. attribute:: attrs
    :type: dict

    .. versionadded:: 2.12

    (Optional) A dictionary mapping of HTML attributes and values for the widget
    container element.

Usage:

# admin.py

from django_jsonform.widgets import JSONFormWidget
from myapp.models import ShoppingList


class ShoppingListForm(forms.ModelForm):
    class Meta:
        model = ShoppingList
        fields = '__all__'
        widgets = {
            'items': JSONFormWidget(schema=ShoppingList.ITEMS_SCHEMA)
        }

class ShoppingListAdmin(admin.ModelAdmin):
    form = ShoppingListForm

admin.site.register(ShoppingList, ShoppingListAdmin)

This widget can not be used directly with Django's ArrayField because Django's ArrayField converts the value from array to a string before passing it to the widget whereas it expects a list or a dict.

Accessing model instance in callable schema

.. versionadded:: 2.8

Automatically accessing model instance in a widget is not possible. This is due the way Django initialises the widgets and form fields.

However, you can bypass this limitation by manually setting an instance attribute on the widget.

To do this, you are required to create a custom form class for your model:

# models.py

def callable_schema(instance=None):
    # instance will be None while creating new object

    if instance:
        # ... do something with the instance ...
    else:
        # ... do something else ...
    return schema


class MyModel(models.Model):
    my_field = JSONField(schema=callable_schema)


...

# admin.py

# create a custom modelform
class MyModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # manually set the current instance on the widget
        self.fields['my_field'].widget.instance = self.instance


# set the form on the admin class
class MyAdmin(admin.ModelAdmin):
    form = MyModelForm


admin.site.register(MyModel, MyAdmin)

Your callable schema function will get the current model instance on Edit/Change admin page. It will be None on the Add new page (i.e. while creating new objects).

Making the whole JSON form readonly

.. versionadded:: 2.19

It is possible to make the whole JSON form readonly dynamically on a per user or per request basis.

This can be done by setting the disabled attribute on the JSON form field. Note that this attribute is set on the form field itself, not on the field's widget.

You are also required to create a custom form class for your model:

# admin.py

# create a custom modelform
class MyModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.fields['my_json_field'].disabled = True # disable the field


# set the form on the admin class
class MyAdmin(admin.ModelAdmin):
    form = MyModelForm


admin.site.register(MyModel, MyAdmin)

Now the whole form will be rendered in readonly mode.

Security wise this works just as well because the disabled attribute on a form field tells Django to ignore that field's value on form submission. See also: Django docs on Field.disabled.