Open pneudecorb opened 4 years ago
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.
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.
...
{% if field.is_readonly %}
<div class="readonly">{{ field.contents }}</div>
{% else %}
{{ field.field }}
{% endif %}
...
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)
<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>
class PayloadAdmin(JSONAdminMixin, admin.ModelAdmin):
fields = ["payload"]
def payload_display(self, obj):
return self.render_readonly_json("payload", json.dumps(obj.payload))
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"] })}, }