Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #34380 -- Allowed specifying a default URL scheme in forms.URLField. #16614

Merged
merged 1 commit into from
Apr 28, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 17 additions & 3 deletions django/forms/fields.py
Expand Up @@ -10,6 +10,7 @@
import os
import re
import uuid
import warnings
from decimal import Decimal, DecimalException
from io import BytesIO
from urllib.parse import urlsplit, urlunsplit
Expand Down Expand Up @@ -42,6 +43,7 @@
)
from django.utils import formats
from django.utils.dateparse import parse_datetime, parse_duration
from django.utils.deprecation import RemovedInDjango60Warning
from django.utils.duration import duration_string
from django.utils.ipv6 import clean_ipv6_address
from django.utils.regex_helper import _lazy_re_compile
Expand Down Expand Up @@ -753,7 +755,19 @@ class URLField(CharField):
}
default_validators = [validators.URLValidator()]

def __init__(self, **kwargs):
def __init__(self, *, assume_scheme=None, **kwargs):
if assume_scheme is None:
warnings.warn(
"The default scheme will be changed from 'http' to 'https' in Django "
"6.0. Pass the forms.URLField.assume_scheme argument to silence this "
"warning.",
RemovedInDjango60Warning,
stacklevel=2,
)
assume_scheme = "http"
# RemovedInDjango60Warning: When the deprecation ends, replace with:
# self.assume_scheme = assume_scheme or "https"
self.assume_scheme = assume_scheme
felixxm marked this conversation as resolved.
Show resolved Hide resolved
super().__init__(strip=True, **kwargs)

def to_python(self, value):
Expand All @@ -773,8 +787,8 @@ def split_url(url):
if value:
url_fields = split_url(value)
if not url_fields[0]:
# If no URL scheme given, assume http://
url_fields[0] = "http"
# If no URL scheme given, add a scheme.
url_fields[0] = self.assume_scheme
if not url_fields[1]:
# Assume that if no domain is provided, that the path segment
# contains the domain.
Expand Down
3 changes: 3 additions & 0 deletions docs/internals/deprecation.txt
Expand Up @@ -32,6 +32,9 @@ details on these changes.

* The ``ForeignObject.get_reverse_joining_columns()`` method will be removed.

* The default scheme for ``forms.URLField`` will change from ``"http"`` to
``"https"``.

.. _deprecation-removed-in-5.1:

5.1
Expand Down
14 changes: 12 additions & 2 deletions docs/ref/forms/fields.txt
Expand Up @@ -1071,8 +1071,18 @@ For each field, we describe the default widget used if you don't specify
given value is a valid URL.
* Error message keys: ``required``, ``invalid``

Has the optional arguments ``max_length``, ``min_length``, and
``empty_value`` which work just as they do for :class:`CharField`.
Has the optional arguments ``max_length``, ``min_length``, ``empty_value``
which work just as they do for :class:`CharField`, and ``assume_scheme``
that defaults to ``"http"``.

.. versionchanged:: 5.0

The ``assume_scheme`` argument was added.

.. deprecated:: 5.0

The default value for ``assume_scheme`` will change from ``"http"`` to
``"https"`` in Django 6.0.

``UUIDField``
-------------
Expand Down
6 changes: 6 additions & 0 deletions docs/releases/5.0.txt
Expand Up @@ -245,6 +245,9 @@ Forms
:ref:`Choices classes <field-choices-enum-types>` directly instead of
requiring expansion with the ``choices`` attribute.

* The new ``assume_scheme`` argument for :class:`~django.forms.URLField` allows
specifying a default URL scheme.

Generic Views
~~~~~~~~~~~~~

Expand Down Expand Up @@ -403,6 +406,9 @@ Miscellaneous

* The ``ForeignObject.get_reverse_joining_columns()`` method is deprecated.

* The default scheme for ``forms.URLField`` will change from ``"http"`` to
``"https"`` in Django 6.0.

Features removed in 5.0
=======================

Expand Down
7 changes: 7 additions & 0 deletions tests/admin_views/tests.py
Expand Up @@ -26,6 +26,7 @@
from django.template.response import TemplateResponse
from django.test import (
TestCase,
ignore_warnings,
modify_settings,
override_settings,
skipUnlessDBFeature,
Expand All @@ -34,6 +35,7 @@
from django.urls import NoReverseMatch, resolve, reverse
from django.utils import formats, translation
from django.utils.cache import get_max_age
from django.utils.deprecation import RemovedInDjango60Warning
from django.utils.encoding import iri_to_uri
from django.utils.html import escape
from django.utils.http import urlencode
Expand Down Expand Up @@ -6555,6 +6557,7 @@ def setUpTestData(cls):
def setUp(self):
self.client.force_login(self.superuser)

@ignore_warnings(category=RemovedInDjango60Warning)
felixxm marked this conversation as resolved.
Show resolved Hide resolved
def test_readonly_get(self):
response = self.client.get(reverse("admin:admin_views_post_add"))
self.assertNotContains(response, 'name="posted"')
Expand Down Expand Up @@ -6615,6 +6618,7 @@ def test_readonly_get(self):
)
self.assertContains(response, "%d amount of cool" % p.pk)

@ignore_warnings(category=RemovedInDjango60Warning)
def test_readonly_text_field(self):
p = Post.objects.create(
title="Readonly test",
Expand All @@ -6634,6 +6638,7 @@ def test_readonly_text_field(self):
# Checking readonly field in inline.
self.assertContains(response, "test<br>link")

@ignore_warnings(category=RemovedInDjango60Warning)
def test_readonly_post(self):
data = {
"title": "Django Got Readonly Fields",
Expand Down Expand Up @@ -6774,6 +6779,7 @@ def test_readonly_onetoone_backwards_ref(self):
field = self.get_admin_readonly_field(response, "plotdetails")
self.assertEqual(field.contents(), "-") # default empty value

@ignore_warnings(category=RemovedInDjango60Warning)
def test_readonly_field_overrides(self):
"""
Regression test for #22087 - ModelForm Meta overrides are ignored by
Expand Down Expand Up @@ -7233,6 +7239,7 @@ def setUpTestData(cls):
def setUp(self):
self.client.force_login(self.superuser)

@ignore_warnings(category=RemovedInDjango60Warning)
def test_field_prefix_css_classes(self):
"""
Fields have a CSS class name with a 'field-' prefix.
Expand Down
7 changes: 6 additions & 1 deletion tests/admin_widgets/tests.py
Expand Up @@ -22,9 +22,10 @@
ManyToManyField,
UUIDField,
)
from django.test import SimpleTestCase, TestCase, override_settings
from django.test import SimpleTestCase, TestCase, ignore_warnings, override_settings
from django.urls import reverse
from django.utils import translation
from django.utils.deprecation import RemovedInDjango60Warning

from .models import (
Advisor,
Expand Down Expand Up @@ -106,6 +107,7 @@ def test_TimeField(self):
def test_TextField(self):
self.assertFormfield(Event, "description", widgets.AdminTextareaWidget)

@ignore_warnings(category=RemovedInDjango60Warning)
def test_URLField(self):
self.assertFormfield(Event, "link", widgets.AdminURLFieldWidget)

Expand Down Expand Up @@ -320,6 +322,7 @@ class AdminForeignKeyRawIdWidget(TestDataMixin, TestCase):
def setUp(self):
self.client.force_login(self.superuser)

@ignore_warnings(category=RemovedInDjango60Warning)
def test_nonexistent_target_id(self):
band = Band.objects.create(name="Bogey Blues")
pk = band.pk
Expand All @@ -335,6 +338,7 @@ def test_nonexistent_target_id(self):
"Select a valid choice. That choice is not one of the available choices.",
)

@ignore_warnings(category=RemovedInDjango60Warning)
def test_invalid_target_id(self):
for test_str in ("Iñtërnâtiônàlizætiøn", "1234'", -1234):
# This should result in an error message, not a server exception.
Expand Down Expand Up @@ -1610,6 +1614,7 @@ def test_refresh_page(self):
self.assertCountSeleniumElements("#id_students_to > option", 2)


@ignore_warnings(category=RemovedInDjango60Warning)
class AdminRawIdWidgetSeleniumTests(AdminWidgetSeleniumTestCase):
def setUp(self):
super().setUp()
Expand Down
35 changes: 30 additions & 5 deletions tests/forms_tests/field_tests/test_urlfield.py
@@ -1,10 +1,12 @@
from django.core.exceptions import ValidationError
from django.forms import URLField
from django.test import SimpleTestCase
from django.test import SimpleTestCase, ignore_warnings
from django.utils.deprecation import RemovedInDjango60Warning

from . import FormFieldAssertionsMixin


@ignore_warnings(category=RemovedInDjango60Warning)
class URLFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_urlfield_widget(self):
f = URLField()
Expand All @@ -26,7 +28,9 @@ def test_urlfield_widget_max_min_length(self):
f.clean("http://abcdefghijklmnopqrstuvwxyz.com")

def test_urlfield_clean(self):
f = URLField(required=False)
# RemovedInDjango60Warning: When the deprecation ends, remove the
# assume_scheme argument.
f = URLField(required=False, assume_scheme="https")
tests = [
("http://localhost", "http://localhost"),
("http://example.com", "http://example.com"),
Expand All @@ -38,8 +42,8 @@ def test_urlfield_clean(self):
"http://example.com?some_param=some_value",
"http://example.com?some_param=some_value",
),
("valid-with-hyphens.com", "http://valid-with-hyphens.com"),
("subdomain.domain.com", "http://subdomain.domain.com"),
("valid-with-hyphens.com", "https://valid-with-hyphens.com"),
("subdomain.domain.com", "https://subdomain.domain.com"),
("http://200.8.9.10", "http://200.8.9.10"),
("http://200.8.9.10:8000/test", "http://200.8.9.10:8000/test"),
("http://valid-----hyphens.com", "http://valid-----hyphens.com"),
Expand All @@ -49,7 +53,7 @@ def test_urlfield_clean(self):
),
(
"www.example.com/s/http://code.djangoproject.com/ticket/13804",
"http://www.example.com/s/http://code.djangoproject.com/ticket/13804",
"https://www.example.com/s/http://code.djangoproject.com/ticket/13804",
),
# Normalization.
("http://example.com/ ", "http://example.com/"),
Expand Down Expand Up @@ -135,3 +139,24 @@ def test_urlfield_unable_to_set_strip_kwarg(self):
msg = "__init__() got multiple values for keyword argument 'strip'"
with self.assertRaisesMessage(TypeError, msg):
URLField(strip=False)

def test_urlfield_assume_scheme(self):
f = URLField()
# RemovedInDjango60Warning: When the deprecation ends, replace with:
# "https://example.com"
self.assertEqual(f.clean("example.com"), "http://example.com")
f = URLField(assume_scheme="http")
self.assertEqual(f.clean("example.com"), "http://example.com")
f = URLField(assume_scheme="https")
self.assertEqual(f.clean("example.com"), "https://example.com")


class URLFieldAssumeSchemeDeprecationTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_urlfield_raises_warning(self):
msg = (
"The default scheme will be changed from 'http' to 'https' in Django 6.0. "
"Pass the forms.URLField.assume_scheme argument to silence this warning."
)
with self.assertWarnsMessage(RemovedInDjango60Warning, msg):
f = URLField()
self.assertEqual(f.clean("example.com"), "http://example.com")
6 changes: 4 additions & 2 deletions tests/forms_tests/tests/test_error_messages.py
Expand Up @@ -23,7 +23,8 @@
utils,
)
from django.template import Context, Template
from django.test import SimpleTestCase, TestCase
from django.test import SimpleTestCase, TestCase, ignore_warnings
from django.utils.deprecation import RemovedInDjango60Warning
from django.utils.safestring import mark_safe

from ..models import ChoiceModel
Expand Down Expand Up @@ -167,7 +168,8 @@ def test_urlfield(self):
"invalid": "INVALID",
"max_length": '"%(value)s" has more than %(limit_value)d characters.',
}
f = URLField(error_messages=e, max_length=17)
with ignore_warnings(category=RemovedInDjango60Warning):
f = URLField(error_messages=e, max_length=17)
self.assertFormErrors(["REQUIRED"], f.clean, "")
self.assertFormErrors(["INVALID"], f.clean, "abc.c")
self.assertFormErrors(
Expand Down
15 changes: 14 additions & 1 deletion tests/generic_inline_admin/tests.py
Expand Up @@ -5,8 +5,15 @@
from django.contrib.contenttypes.models import ContentType
from django.forms.formsets import DEFAULT_MAX_NUM
from django.forms.models import ModelForm
from django.test import RequestFactory, SimpleTestCase, TestCase, override_settings
from django.test import (
RequestFactory,
SimpleTestCase,
TestCase,
ignore_warnings,
override_settings,
)
from django.urls import reverse
from django.utils.deprecation import RemovedInDjango60Warning

from .admin import MediaInline, MediaPermanentInline
from .admin import site as admin_site
Expand All @@ -21,6 +28,7 @@ def setUpTestData(cls):
)


@ignore_warnings(category=RemovedInDjango60Warning)
@override_settings(ROOT_URLCONF="generic_inline_admin.urls")
class GenericAdminViewTest(TestDataMixin, TestCase):
def setUp(self):
Expand Down Expand Up @@ -95,6 +103,7 @@ def test_basic_edit_POST(self):
self.assertEqual(response.status_code, 302) # redirect somewhere


@ignore_warnings(category=RemovedInDjango60Warning)
@override_settings(ROOT_URLCONF="generic_inline_admin.urls")
class GenericInlineAdminParametersTest(TestDataMixin, TestCase):
factory = RequestFactory()
Expand Down Expand Up @@ -296,6 +305,7 @@ def test_delete(self):

@override_settings(ROOT_URLCONF="generic_inline_admin.urls")
class NoInlineDeletionTest(SimpleTestCase):
@ignore_warnings(category=RemovedInDjango60Warning)
def test_no_deletion(self):
inline = MediaPermanentInline(EpisodePermanent, admin_site)
fake_request = object()
Expand All @@ -321,6 +331,7 @@ class GenericInlineModelAdminTest(SimpleTestCase):
def setUp(self):
self.site = AdminSite()

@ignore_warnings(category=RemovedInDjango60Warning)
def test_get_formset_kwargs(self):
media_inline = MediaInline(Media, AdminSite())

Expand Down Expand Up @@ -360,6 +371,7 @@ class EpisodeAdmin(admin.ModelAdmin):
["keywords", "id", "DELETE"],
)

@ignore_warnings(category=RemovedInDjango60Warning)
def test_custom_form_meta_exclude(self):
"""
The custom ModelForm's `Meta.exclude` is respected by
Expand Down Expand Up @@ -403,6 +415,7 @@ class EpisodeAdmin(admin.ModelAdmin):
["description", "keywords", "id", "DELETE"],
)

@ignore_warnings(category=RemovedInDjango60Warning)
def test_get_fieldsets(self):
# get_fieldsets is called when figuring out form fields.
# Refs #18681.
Expand Down