Repository: wagtail/wagtail

Issue:
Multiple errors shown when editing a Site to use existing hostname and port
<!--
Found a bug? Please fill out the sections below. 👍
-->

### Issue Summary

<!--
A summary of the issue.
-->

When editing a Site and using a hostname and port combination that already exist, an error stating "Site with this Hostname and Port already exists." is shown. However, this error is shown both at the top of the page (using the messages framework) and an ad-hoc rendering in the template.

<img width="943" alt="image" src="https://github.com/wagtail/wagtail/assets/6379424/9cc14127-bfa0-4102-8e25-5f16e4f33e07">

This doesn't happen on the create view:

<img width="943" alt="image" src="https://github.com/wagtail/wagtail/assets/6379424/4ab648e5-a6ce-494b-b486-95253fa4daed">


### Steps to Reproduce

1. Spin up bakerydemo
2. Create a new Site instance via http://localhost:8000/admin/sites/new
3. Once created, update the new site instance to use the same hostname and port combination as another one e.g. the default `127.0.0.1` and `8000`

### Details

This was introduced in #6086. That PR was created years ago, before the generic views make use of [`messages.validation_error`](https://github.com/wagtail/wagtail/blob/e16614bce8c2433e012df2d3f60b1dde9465b7de/wagtail/admin/messages.py#L38-L62) that we created (in 4a5714d5c7b11525e99c14ec13e69351a42aaeef) to specifically handle non-field errors in forms:
- https://github.com/wagtail/wagtail/commit/ec70921e52b21c0ab463d1f243a2fc9c64c4e573#diff-e1bcd86f7d82181d10043d53e7cc8eda34f657fe28c7a75c3768b707ad000e10L192
- https://github.com/wagtail/wagtail/commit/03fc621129e6256a42c0c0d118b5312792825a44#diff-e1bcd86f7d82181d10043d53e7cc8eda34f657fe28c7a75c3768b707ad000e10L267

However, it was merged after #8332 and #8344 were merged, so it didn't make use of the `messages.validation_error` function.

Per the comment in https://github.com/wagtail/wagtail/issues/536#issuecomment-285685185, the solution in #6086 shouldn't have added the `non_field_errors` rendering in the generic form template:
https://github.com/wagtail/wagtail/blob/ade573bce28b43bd62bab017631c0d91d367ad85/wagtail/admin/templates/wagtailadmin/generic/form.html#L9-L13

Instead, it should make use of the way we render messages in the base admin template via Django's messages framework. This already works for edit view (hence the error shown twice), but for the create view this isn't triggered due to a missing `error_message` in the `CreateView` subclass:

https://github.com/wagtail/wagtail/blob/ade573bce28b43bd62bab017631c0d91d367ad85/wagtail/sites/views.py#L32-L41

So the view's `form_invalid` doesn't call `messages.validation_error`:

https://github.com/wagtail/wagtail/blob/536ad2891e90a9ad6fcd049592ce890adfca4332/wagtail/admin/views/generic/models.py#L651-L656

### Solution

We should remove the ad-hoc `non_field_errors` rendering from the generic form template, and add an `error_message` to the sites `CreateView`.


Discussion:
I would like to work on this issue.
I'll start by reproducing it so we can mark it confirmed.

I can confirm that the issue exists. A screenshot for reference is attached below.

**Screenshot of issue**

![image](https://github.com/wagtail/wagtail/assets/111359305/2cfd6526-b03b-4830-ad09-2ccf1611422b)

I'll try to resolve this using suggested approach!

Resolving patch:
diff --git a/wagtail/sites/views.py b/wagtail/sites/views.py
--- a/wagtail/sites/views.py
+++ b/wagtail/sites/views.py
@@ -32,6 +32,7 @@ class IndexView(generic.IndexView):
 class CreateView(generic.CreateView):
     page_title = _("Add site")
     success_message = _("Site '%(object)s' created.")
+    error_message = _("The site could not be saved due to errors.")
 
 
 class EditView(generic.EditView):


Test patch:
diff --git a/wagtail/sites/tests.py b/wagtail/sites/tests.py
--- a/wagtail/sites/tests.py
+++ b/wagtail/sites/tests.py
@@ -151,12 +151,17 @@ def test_non_field_errors_are_displayed(self):
             {"hostname": "localhost", "port": "80", "root_page": str(self.home_page.id)}
         )
         expected_html = """
-            <div class="help-block help-critical">
-                <svg class="icon icon-warning icon" aria-hidden="true">
+            <li class="error">
+                <svg class="class="icon icon-warning messages-icon"" aria-hidden="true">
                     <use href="#icon-warning"></use>
                 </svg>
+                The site could not be saved due to errors.
+                <ul class="errorlist">
+                <li>
                 Site with this Hostname and Port already exists.
-            </div>
+                </li>
+                </ul>
+            </li>
         """
         self.assertTagInHTML(expected_html, response.content.decode())
 
@@ -314,12 +319,17 @@ def test_non_field_errors_are_displayed(self):
             site_id=second_site.id,
         )
         expected_html = """
-            <div class="help-block help-critical">
-                <svg class="icon icon-warning icon" aria-hidden="true">
+            <li class="error">
+                <svg class="class="icon icon-warning messages-icon"" aria-hidden="true">
                     <use href="#icon-warning"></use>
                 </svg>
+                The site could not be saved due to errors.
+                <ul class="errorlist">
+                <li>
                 Site with this Hostname and Port already exists.
-            </div>
+                </li>
+                </ul>
+            </li>
         """
         self.assertTagInHTML(expected_html, response.content.decode())