Reputation: 281
I am using wagtail ModelAdmin
for some of my non page models and want to add some custom validation.
This is some of the code.
class EditPlanningView(EditView):
def publish_url(self):
return self.url_helper.get_action_url('publish', self.pk_quoted)
def unpublish_url(self):
return self.url_helper.get_action_url('unpublish', self.pk_quoted)
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
instance = form.save(commit=False)
if bool(request.POST.get('action-publish')):
try:
instance.publish(commit=True)
except PublishWithoutMeetingError as e:
form.add_error(
'planning_meeting',
e
)
return self.form_invalid(form)
When validation fails the invalid form is returned, but the error I added is not bound to the field. In stead a 'general error message' appears at the top.
Can someone help me out?
Cheers,
Robert
Upvotes: 2
Views: 1531
Reputation: 5055
I think the error is in the following lines.
form.add_error(
'planning_meeting',
e
)
Actually can't say anything without knowing about PublishWithoutMeetingError
, the type of e
. Better to replace e
with a string. And make sure the post
method is not throwing any exceptions. Other than that, what you have done is correct. Read the following to also to check if you have missed any point.
There are two ways that you can achieve showing an error messages in forms.
In both of these cases, you are going to use a method called add_error
. That method takes 2 argument, field
and error
. From these two, error
is the most important argument. The field
simply state the field of the form that this error applies to. This can be None
.
The error
argument can be multiple types.
error
argument can be an instance of str
. Then wagtail will assign the given error
to the given field
.error
argument can be an instance of list
of str
. Then wagtail will assign the given list of errors to the given field
.error
argument can be an instance of dict
with str
keys and str
or list
of str
values. In this case field
should be None
. The keys will be used as the fields for the errors given by values.error
argument can be an instance of ValidationError
exception. You can create a ValidationError
using a str
, list
, or dict
, which represent the above three cases.In the form clean
method need to be overridden in order to find errors.
from wagtail.admin.forms.models import WagtailAdminModelForm
class ExtraForm(WagtailAdminModelForm):
def clean(self):
cleaned_data = super().clean() # Get the already cleaned data. Same as self.cleaned_data in this case. But this way is better.
title = cleaned_data.get('title') # Get the cleaned title
if title is None: # Title is never None here, but still..
return cleaned_data
title = title.strip() # Do some formatting if needed
if title.startswith('A'): # Validation
self.add_error('title', 'Title cannot start with A') # Validation error
else:
cleaned_data['title'] = title # Use the formatted title
return cleaned_data
class MyModel(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=500, default='', blank=False)
# Or any other fields you have
base_form_class = ExtraForm # Tell wagtail to use ExtraForm instead of the default one
This way is same as the way that you have mentioned in the question. You need to override post
method. You need to check if the form
associated with the EditView
is valid or invalid and return the appropriate form.
To check validity, is_valid
method of the form is used by default. That method will clean the form and check if there are errors added to the form.
If form is valid, you need to return self.valid_form
and self.invalid_form
otherwise.
Unlike overriding the Form
, you can access the request
here.
class MyEditView(EditView):
def post(self, request, *args, **kwargs):
form = self.get_form() # Get the form associated with this edit view
if form.is_valid(): # Check if the form pass the default checks
my_field = request.POST.get('my_field') # You can access the request
title = form.cleaned_data.get('title') # You can access the form data
if title != my_field: # Validation
form.add_error('title', 'Title must match my_field') # Validation error
return self.form_invalid(form) # Return invalid form if there are validation errors
return self.form_valid(form) # Return the valid form if there are no validation errors
else:
return self.form_invalid(form) # Return invalid form if default check failed
class MyModelAdmin(ModelAdmin):
model = MyModel
menu_label = 'My Model'
list_display = ('id', 'title')
search_fields = (
'title',
)
edit_view_class = MyEditView # Tell wagtail to use MyEditView instead of the default one.
Upvotes: 2