Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions django/contrib/admin/static/admin/css/responsive.css
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ button {
.stacked ul.selector-chooser {
padding: 0 2px;
transform: none;
margin: 10px auto;
}

.stacked .selector-chooser li {
Expand Down Expand Up @@ -571,6 +572,7 @@ button {
height: 30px;
padding: 0 2px;
transform: none;
margin: auto;
}

.selector ul.selector-chooser li {
Expand Down
3 changes: 1 addition & 2 deletions django/contrib/admin/static/admin/css/widgets.css
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@
display: flex;
height: 30px;
width: 64px;
margin: 0 0 10px 40%;
margin: 0 auto 10px auto;
background-color: #eee;
border-radius: 10px;
transform: none;
Expand Down Expand Up @@ -628,7 +628,6 @@ ul.timelist,
.related-widget-wrapper {
display: flex;
gap: 0 10px;
flex-grow: 1;
flex-wrap: wrap;
margin-bottom: 5px;
}
Expand Down
5 changes: 4 additions & 1 deletion django/test/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,10 @@ def is_file(thing):
# Each bit of the multipart form data could be either a form value or a
# file, or a *list* of form values and/or files. Remember that HTTP field
# names can be duplicated!
for key, value in data.items():
# Use lists() if data is a MultiValueDict, so that all values for a key
# are preserved (e.g. a form field and a file sharing the same name).
raw_data = getattr(data, "lists", data.items)()
for key, value in raw_data:
if value is None:
raise TypeError(
"Cannot encode None for key '%s' as POST data. Did you mean "
Expand Down
17 changes: 17 additions & 0 deletions tests/admin_widgets/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1861,6 +1861,23 @@ def test_form_submission_via_enter_key_with_filter_horizontal(self):
school.alumni.all().order_by("name"), [self.lisa, self.peter]
)

@screenshot_cases(["desktop_size", "mobile_size", "rtl", "dark", "high_contrast"])
def test_vertical_arrow_buttons_layout(self):
from selenium.webdriver.common.by import By

self.admin_login(username="super", password="secret", login_url="/")
self.selenium.get(
self.live_server_url
+ reverse("admin:admin_widgets_school_change", args=(self.school.id,))
)
students = self.selenium.find_element(By.CSS_SELECTOR, ".field-students")
self.selenium.execute_script("window.scrollTo(0, %s);" % students.location["y"])
buttons = self.selenium.find_element(
By.CSS_SELECTOR, "div.selector.stacked ul.selector-chooser"
)
self.assertTrue(buttons.is_displayed())
self.take_screenshot("arrow_buttons")


class AdminRawIdWidgetSeleniumTests(AdminWidgetSeleniumTestCase):
def setUp(self):
Expand Down
19 changes: 19 additions & 0 deletions tests/test_client/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

from django.contrib.auth.models import Permission, User
from django.core import mail
from django.core.files.uploadedfile import SimpleUploadedFile
from django.http import HttpResponse, HttpResponseNotAllowed
from django.test import (
AsyncRequestFactory,
Expand All @@ -38,6 +39,7 @@
override_settings,
)
from django.urls import reverse_lazy
from django.utils.datastructures import MultiValueDict
from django.utils.decorators import async_only_middleware
from django.views.generic import RedirectView

Expand Down Expand Up @@ -1045,6 +1047,23 @@ def test_uploading_named_temp_file(self):
)
self.assertEqual(response.content, b"named_temp_file")

def test_uploading_file_and_field_with_same_name(self):
"""
A form field and file uploads sharing the same name in a
MultiValueDict are both preserved when posted by the test client.
"""
file = SimpleUploadedFile("file.txt", b"content")
response = self.client.post(
"/upload_view/",
data=MultiValueDict({"shared": ["field_value", file]}),
)
request = response.wsgi_request
self.assertEqual(request.POST.getlist("shared"), ["field_value"])
self.assertEqual(
[f.name for f in request.FILES.getlist("shared")],
["file.txt"],
)

def test_query_params(self):
tests = (
"get",
Expand Down
Loading