[Django]-Is there any way to make the wagtail body field readonly?

1👍

Proposed Solution

  • The Wagtail documentation regarding Customising generated forms explains the method to override the form that gets generated when editing/creating a new page.
  • The WagtailAdminPageForm extends the Django ModelForm and you can extend this further to add custom clean/__init__/save etc methods to add essentially any logic you want to both how the form renders and what errors get provided to the user before the save gets applied.
  • Django ModelForm documentation.
  • By default you do not have acesss to the request object on form creation, but you do get it on the save method so it would be possible to easily do some user basic logic there.
  • If you need further customisation, you can dig into Wagtail edit handers (search through the source code) and you can create your own edit handler that can pass in the request to your custom BlogPageForm.
  • Note: If the eventual goal is to add a full on ‘process’ based page editing workflow, you may want to look at Wagtails’ ModelAdmin and essentially just build the blog workflow completely in isolation of the normal page structure and then restructure permissions so that blog editors cannot access the normal page tree but can only access your custom workflow.

Example Code

  • This is just a basic example of a custom form for a BlogPage model.
  • __init__ can be extended to add custom logic to how the form gets generated (e.g. make some fields read only or even ‘hide’ some fields).
  • save can be extended to add server side validation to the read only fields and also provide user facing error messaging.
  • It is possible to add logic for a ‘new’ page creation along with logic for editing an existing page by checking if the self.instance.pk (primary key) exists.
# other imports
from wagtail.admin.forms import WagtailAdminPageForm


class BlogPageForm(WagtailAdminPageForm):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if not instance.pk:
            # this is a NEW blog entry form - only allow title to be enabled, disable other fields
            self.fields['description'].widget.attrs['readonly'] = True
        if instance.pk:
            # assume title has been entered and saved at this point (required for a new post)
            # disable the title field
            self.fields['title'].widget.attrs['readonly'] = True

    def clean(self):
        cleaned_data = super().clean()
        instance = getattr(self, 'instance', None)

        title = cleaned_data['title']
        description = cleaned_data['description']
        if not instance.pk:
            # this is a NEW blog entry, check that only the title has been entered
            if not title:
                self.add_error('title', 'title must be edited before all other fields')
                return cleaned_data
            if description:
                self.add_error('description', 'description cannot be entered until title has been completed')

        if instance.pk:
            # an existing blog entry, do not allow title to be changed
            print('title', instance.title, title)
            if instance.title != title:
                self.add_error('title', 'title cannot be edited after the initial save')


class BlogPage(Page):
    # ...fields

    base_form_class = BlogPageForm

1👍

Can be done easy with additional custom.css file

  1. With insert_global_admin_css Wagtail hook, add path to your custom.css file. Here is link for documentation: https://docs.wagtail.io/en/latest/reference/hooks.html#insert-global-admin-css

  2. Then add classname(eg. "myReadonlyInput") to FieldPanel in Page model. This will add new class to li element with input field.

    FieldPanel("field_name", classname="myReadonlyInput"),

  3. In custom.css file add pointer-events:none; to input field that belongs to new class for li element:

    li.myReadonlyInput  div.input input {
        background-color: rgb(239 239 239);
        color: rgb(99 99 99);
        pointer-events:none;
        cursor:text;
    }
    

That way only with adding classname to any field model, input field will be grayed out and not reachable.

Leave a comment