Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Widget doesn't appear to support "disabled" behavior #38

Open
pneudecorb opened this issue Mar 13, 2020 · 2 comments
Open

Widget doesn't appear to support "disabled" behavior #38

pneudecorb opened this issue Mar 13, 2020 · 2 comments

Comments

@pneudecorb
Copy link

pneudecorb commented Mar 13, 2020

  • django-json-widget version: 1.0
  • Django version: 3.0.4
  • Python version: 3.6.10
  • Operating System: Mac

Description

The widget doesn't appear to support the "disabled" html attribute. When I set associated Django field (e.g., JsonField) to disabled, it doesn't render as disabled and still allows the user to make edits in the widget.

If there is a way to render the widget, but in a "disabled" state, I would like to know how to specify that on the widget.

Work around

Set options to restrict mode to read-only text mode and prevent the user from selecting other modes.
Downside: This affects all JSONFields on the form and may not want ALL to be read-only.
formfield_overrides = {
fields.JSONField: {'widget': JSONEditorWidget(options={"mode": "text", "modes": ["text"] })},
}

@m0hithreddy
Copy link

m0hithreddy commented Sep 25, 2021

I assume you are not adding the desired fields to readonly_fields list/tuple. Because, as soon as you add, they are rendered as plain TextArea. If you are doing it this way, your admin form is open to a security threat. Though at the editor level, there is no option to edit JSON data but think of the user submitting a different payload using raw endpoints. It accepts and modifies! The best way IMHO is:

Define a form for your payload

class PayloadForm(forms.ModelForm):
    """Form for viewing payload"""

    class Meta:
        model = Model
        fields = ('payload',)
        widgets = {
            'payload': JSONEditorWidget(options={
                'mode': 'view',
                'modes': ['view']
            }),
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields.get('payload').disabled = True

Then in your admin

class ModelAdmin(admin.ModelAdmin):
    ""Model Admin"""

    model = Model
    form = PayloadForm
    readonly_fields = (....)  # Do not add 'payload' in this list.

The advantage of the above approach is two-fold, all JSONFields are not getting subjected to a similar setting. And, at the editor level, the user is presented with only the 'view' mode. Even if the user tampers and tries to submit a different payload, the changes are dropped in favor of existing data since we set disabled to True.

@elonzh
Copy link

elonzh commented Nov 2, 2021

Here is my solution:

https://gist.github.com/elonzh/413df4532e491de27f9a51e9dbf7e8c1

Django admin render readonly_fields without widgets, So just remove the textarea input in JSONEditorWidget template and write a customized display function.

django/contrib/admin/templates/admin/includes/fieldset.html

...
{% if field.is_readonly %}
    <div class="readonly">{{ field.contents }}</div>
{% else %}
    {{ field.field }}
{% endif %}
...

JSONAdminMixin

class JSONAdminMixin:
    formfield_overrides = {
        models.JSONField: {"widget": JSONEditorWidget},
    }

    class ReadonlyJSONWidget(JSONEditorWidget):
        template_name = 'django_json_readonly_widget.html'

    @property
    def media(self):
        return super().media + self.ReadonlyJSONWidget().media

    @classmethod
    def render_readonly_json(cls, name, value, attrs=None, mode='code', options=None, width=None, height=None):
        attrs = attrs or {}
        attrs.setdefault("id", "id_" + name)
        widget = cls.ReadonlyJSONWidget(attrs, mode, options, width, height)
        return widget.render(name, value)

django_json_readonly_widget.html

<div {% if not widget.attrs.style %}style="height:{{widget.height|default:'500px'}};width:{{widget.width|default:'90%'}};display:inline-block;"{% endif %}{% include "django/forms/widgets/attrs.html" %}></div>

<script>
    (function() {
        var container = document.getElementById("{{ widget.attrs.id }}");

        var options = {{ widget.options|safe }};
        options.onModeChange = function (newMode, oldMode) {
            if (newMode === 'code') {
                editor.aceEditor.setReadOnly(true);
            }
        }

        var editor = new JSONEditor(container, options);
        if (editor.mode === 'code') {
            editor.aceEditor.setReadOnly(true);
        }
        var json = {{ widget.value|safe }};
        editor.set(json);
    })();
</script>

PayloadAdmin:

class PayloadAdmin(JSONAdminMixin, admin.ModelAdmin):
	fields = ["payload"]
    def payload_display(self, obj):
        return self.render_readonly_json("payload", json.dumps(obj.payload))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants