-
I have a FormCollection with multiple siblings which needs to validate the data across all siblings. If there's an error this needs to be conveyed to the user. Following the approach used by the django-formset source, I have tried using variations of self._errors.append() from the full_clean() method. For example:
When this error is set the server reports "Unprocessable Entity" and returns a 422 (as it should), but the web browser does not show the message anywhere. The template uses "{{ form_collection }}" to render the form. I have tried including
in the template before "{{ form_collection }}" but this makes no difference. In case it makes a difference, the FormCollection concerned is included in another FormCollection, which is then set as the collection_class of a EditCollectionView. How should one go about propagating messages from a FormCollection full_clean() method to the browser display? |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 1 reply
-
From what I see in your code, you want to show a "collection error". Does the rendered HTML include a placeholder such as <div role="alert" class="dj-collection-errors" prefix="some_prefix"><ul class="dj-errorlist"></ul></div> In the browser's developer tools, check the response of your POST (422-code). Does this contain the error message? If the prefixes of that error message match the prefix of the placeholder, it should be rendered. Otherwise, please fork this project and add your models, collections and forms, so that I can reproduce it. |
Beta Was this translation helpful? Give feedback.
-
Thanks for the response. The rendered HTML does include something similar to the placeholder you mentioned if I put
somewhere in the template (which I have done as noted before). However, it does not exactly match what you have suggested. It is instead
I guess the inclusion of formset/non_field_errors.html is incorrect and that I should probably try just manually putting You mentioned that this use case counts as a "collection error". How should the self._errors.append() call be structured to produce one of these? I was having a poke around the browser debug tools just prior to noticing your response. The browser is receiving a 422 and the error message is present. I therefore suspect that the problem is in the way my self._errors.append() call is structured. The raw response received is
What part of this counts as the prefix you mentioned? |
Beta Was this translation helpful? Give feedback.
-
All non-field errors should be shown as near as possible as where they occur. form-errors, if the I can check this for you, if you create a pull request for me. Maybe its a bug on my side or a misconfiguration on yours. |
Beta Was this translation helpful? Give feedback.
-
I think I now have an understanding about most of what I reported and observed yesterday. Thanks for the hints which lead to these discoveries. On reflection I do not understand why it seemed I needed the manual '<div role="alert"...>' entry in the template. I subsequently noticed that there was a similar tag generated amongst the output produced by '{{ form_collection }}'. When I removed the manual tag the error messages were still shown. I therefore suspect that was a red herring. It probably appeared to make things work due to near-simultaneous changes to the format of the error passed to self._errors.append(). With no added tag in the template, calling
in the FormCollection full_clean() method produced the error text above the relevant form collection. While some Django form classes use clean() for validation, FormCollection calls full_clean() instead. I haven't yet discovered the circumstances in which the FormCollection clean() method is called. It may be a placeholder, since all it does is return self.clean_data. The COLLECTION_ERRORS errors were not subject to the submit button's scrollToError action because they do not result in a form field being set as being in error. As a result there is no form field to scroll to. To address this I shifted to using a NON_FORM_FIELD error instead:
With this in place, the messages were still shown in a reasonable place and the browser scrolled to the first message in the event of an error. The scrolling happens because the error condition is attached to the form, which counts as an element for the purposes of the scrollToError typescript code. This approach more or less answers the original question. Ultimately, my original attempt got the leading key string wrong in the call to self._errors.append() and I didn't have any idea what it ought to be. Rather than "alert", it needed to be "address_form", where "address_form" is the name of the form instance variable within the FormCollection:
I hope this information helps others in future. I did try attaching the message to a specific field:
This also worked, but created two undesirable side effects. Firstly, a small dark grey window with rounded corners appeared above the error messages, containing copies of the error messages. Secondly, if the form was then immediately resubmitted, the browser's validation system falsely concluded that some checkboxes weren't selected when in fact they were. Third, if the '[" ... "]' list had more than one string item then only the first was displayed by the browser. Of course all these are of peripheral interest to the topic at hand. I may investigate them in more detail later if they end up being relevant to my current use case. |
Beta Was this translation helpful? Give feedback.
I think I now have an understanding about most of what I reported and observed yesterday. Thanks for the hints which lead to these discoveries.
On reflection I do not understand why it seemed I needed the manual '<div role="alert"...>' entry in the template. I subsequently noticed that there was a similar tag generated amongst the output produced by '{{ form_collection }}'. When I removed the manual tag the error messages were still shown. I therefore suspect that was a red herring. It probably appeared to make things work due to near-simultaneous changes to the format of the error passed to self._errors.append().
With no added tag in the template, calling