diff options
| author | Jon Dufresne <jon.dufresne@gmail.com> | 2020-03-13 06:26:06 -0700 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2020-03-16 07:31:19 +0100 |
| commit | 3857a08bdb05e30f90f56a7dd0d505ad19f4c403 (patch) | |
| tree | ba0dcd03fab679b4c0aab33561e32b97f418efee | |
| parent | b7093860df7bcdc3b8fafa9e6b206137da66d89b (diff) | |
Fixed #31361 -- Fixed invalid action="" in admin forms.
The attribute action="" (empty string) on the <form> element is invalid
HTML5. The spec (https://html.spec.whatwg.org/#attr-fs-action) says:
> The action and formaction content attributes, if specified, must have
> a value that is a valid non-empty URL potentially surrounded by
> spaces.
Emphasis on non-empty. The action attribute is allowed to be omitted, in
which case the current URL is used which is the same behavior as now.
| -rw-r--r-- | django/contrib/admin/templates/admin/auth/user/change_password.html | 2 | ||||
| -rw-r--r-- | django/contrib/admin/templates/admin/change_form.html | 2 | ||||
| -rw-r--r-- | tests/admin_views/tests.py | 32 | ||||
| -rw-r--r-- | tests/auth_tests/test_views.py | 3 |
4 files changed, 24 insertions, 15 deletions
diff --git a/django/contrib/admin/templates/admin/auth/user/change_password.html b/django/contrib/admin/templates/admin/auth/user/change_password.html index 2d723cf0d6..1c3347e659 100644 --- a/django/contrib/admin/templates/admin/auth/user/change_password.html +++ b/django/contrib/admin/templates/admin/auth/user/change_password.html @@ -19,7 +19,7 @@ {% endblock %} {% endif %} {% block content %}<div id="content-main"> -<form action="{{ form_url }}" method="post" id="{{ opts.model_name }}_form">{% csrf_token %}{% block form_top %}{% endblock %} +<form{% if form_url %} action="{{ form_url }}"{% endif %} method="post" id="{{ opts.model_name }}_form">{% csrf_token %}{% block form_top %}{% endblock %} <input type="text" name="username" value="{{ original.get_username }}" style="display: none"> <div> {% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %} diff --git a/django/contrib/admin/templates/admin/change_form.html b/django/contrib/admin/templates/admin/change_form.html index f1f317806d..fe19b40e85 100644 --- a/django/contrib/admin/templates/admin/change_form.html +++ b/django/contrib/admin/templates/admin/change_form.html @@ -33,7 +33,7 @@ </ul> {% endif %}{% endif %} {% endblock %} -<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.model_name }}_form" novalidate>{% csrf_token %}{% block form_top %}{% endblock %} +<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}{% if form_url %}action="{{ form_url }}" {% endif %}method="post" id="{{ opts.model_name }}_form" novalidate>{% csrf_token %}{% block form_top %}{% endblock %} <div> {% if is_popup %}<input type="hidden" name="{{ is_popup_var }}" value="1">{% endif %} {% if to_field %}<input type="hidden" name="{{ to_field_var }}" value="{{ to_field }}">{% endif %} diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 833a0cf2c8..d903261052 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -5868,21 +5868,19 @@ class AdminKeepChangeListFiltersTests(TestCase): self.get_changelist_filters_querystring(), ) - def get_add_url(self): - return '%s?%s' % ( - reverse('admin:auth_user_add', - current_app=self.admin_site.name), - self.get_preserved_filters_querystring(), - ) + def get_add_url(self, add_preserved_filters=True): + url = reverse('admin:auth_user_add', current_app=self.admin_site.name) + if add_preserved_filters: + url = '%s?%s' % (url, self.get_preserved_filters_querystring()) + return url - def get_change_url(self, user_id=None): + def get_change_url(self, user_id=None, add_preserved_filters=True): if user_id is None: user_id = self.get_sample_user_id() - return "%s?%s" % ( - reverse('admin:auth_user_change', args=(user_id,), - current_app=self.admin_site.name), - self.get_preserved_filters_querystring(), - ) + url = reverse('admin:auth_user_change', args=(user_id,), current_app=self.admin_site.name) + if add_preserved_filters: + url = '%s?%s' % (url, self.get_preserved_filters_querystring()) + return url def get_history_url(self, user_id=None): if user_id is None: @@ -5965,6 +5963,11 @@ class AdminKeepChangeListFiltersTests(TestCase): self.assertRedirects(response, self.get_add_url()) post_data.pop('_addanother') + def test_change_view_without_preserved_filters(self): + response = self.client.get(self.get_change_url(add_preserved_filters=False)) + # The action attribute is omitted. + self.assertContains(response, '<form method="post" id="user_form" novalidate>') + def test_add_view(self): # Get the `add_view`. response = self.client.get(self.get_add_url()) @@ -6003,6 +6006,11 @@ class AdminKeepChangeListFiltersTests(TestCase): self.assertRedirects(response, self.get_add_url()) post_data.pop('_addanother') + def test_add_view_without_preserved_filters(self): + response = self.client.get(self.get_add_url(add_preserved_filters=False)) + # The action attribute is omitted. + self.assertContains(response, '<form method="post" id="user_form" novalidate>') + def test_delete_view(self): # Test redirect on "Delete". response = self.client.post(self.get_delete_url(), {'post': 'yes'}) diff --git a/tests/auth_tests/test_views.py b/tests/auth_tests/test_views.py index a5f36bb9ea..f33cbc8382 100644 --- a/tests/auth_tests/test_views.py +++ b/tests/auth_tests/test_views.py @@ -1284,7 +1284,8 @@ class UUIDUserTests(TestCase): password_change_url = reverse('custom_user_admin:auth_user_password_change', args=(u.pk,)) response = self.client.get(password_change_url) - self.assertEqual(response.status_code, 200) + # The action attribute is omitted. + self.assertContains(response, '<form method="post" id="uuiduser_form">') # A LogEntry is created with pk=1 which breaks a FK constraint on MySQL with connection.constraint_checks_disabled(): |
