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
3 changes: 1 addition & 2 deletions .github/workflows/coverage_comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ jobs:
if: >
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success' &&
github.repository == 'django/django' &&
github.event.workflow_run.pull_requests[0] != null
github.repository == 'django/django'
name: Post Coverage Comment
runs-on: ubuntu-latest
timeout-minutes: 60
Expand Down
3 changes: 2 additions & 1 deletion django/contrib/admin/static/admin/css/changelists.css
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,8 @@
font-weight: bold;
}

.change-list .toplinks a {
.change-list .toplinks a,
.change-list .toplinks span {
font-size: 0.8125rem;
}

Expand Down
4 changes: 3 additions & 1 deletion django/contrib/admin/templates/admin/date_hierarchy.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{% load i18n %}
{% if show %}
<nav class="toplinks">
<nav class="toplinks" aria-labelledby="date-hierarchy-label">
<span id="date-hierarchy-label">{% blocktranslate %}Filter by {{ field_name }}{% endblocktranslate %}</span>
{% block date-hierarchy-toplinks %}
{% block date-hierarchy-back %}
{% if back %}<a href="{{ back.link }}" class="date-back">&lsaquo; {{ back.title }}</a>{% endif %}
Expand Down
12 changes: 11 additions & 1 deletion django/contrib/admin/templatetags/admin_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def pagination(cl):
@register.tag(name="pagination")
def pagination_tag(parser, token):
return InclusionAdminNode(
"pagination",
parser,
token,
func=pagination,
Expand Down Expand Up @@ -361,6 +362,7 @@ def result_list(cl):
@register.tag(name="result_list")
def result_list_tag(parser, token):
return InclusionAdminNode(
"result_list",
parser,
token,
func=result_list,
Expand All @@ -376,6 +378,7 @@ def date_hierarchy(cl):
if cl.date_hierarchy:
field_name = cl.date_hierarchy
field = get_fields_from_path(cl.model, field_name)[-1]
field_verbose_name = field.verbose_name
if isinstance(field, models.DateTimeField):
dates_or_datetimes = "datetimes"
else:
Expand Down Expand Up @@ -418,6 +421,7 @@ def link(filters):
"choices": [
{"title": capfirst(formats.date_format(day, "MONTH_DAY_FORMAT"))}
],
"field_name": field_verbose_name,
}
elif year_lookup and month_lookup:
days = getattr(cl.queryset, dates_or_datetimes)(field_name, "day")
Expand All @@ -440,6 +444,7 @@ def link(filters):
}
for day in days
],
"field_name": field_verbose_name,
}
elif year_lookup:
months = getattr(cl.queryset, dates_or_datetimes)(field_name, "month")
Expand All @@ -457,6 +462,7 @@ def link(filters):
}
for month in months
],
"field_name": field_verbose_name,
}
else:
years = getattr(cl.queryset, dates_or_datetimes)(field_name, "year")
Expand All @@ -470,12 +476,14 @@ def link(filters):
}
for year in years
],
"field_name": field_verbose_name,
}


@register.tag(name="date_hierarchy")
def date_hierarchy_tag(parser, token):
return InclusionAdminNode(
"date_hierarchy",
parser,
token,
func=date_hierarchy,
Expand All @@ -500,6 +508,7 @@ def search_form(cl):
@register.tag(name="search_form")
def search_form_tag(parser, token):
return InclusionAdminNode(
"search_form",
parser,
token,
func=search_form,
Expand Down Expand Up @@ -532,14 +541,15 @@ def admin_actions(context):
@register.tag(name="admin_actions")
def admin_actions_tag(parser, token):
return InclusionAdminNode(
parser, token, func=admin_actions, template_name="actions.html"
"admin_actions", parser, token, func=admin_actions, template_name="actions.html"
)


@register.tag(name="change_list_object_tools")
def change_list_object_tools_tag(parser, token):
"""Display the row of change list object tools."""
return InclusionAdminNode(
"change_list_object_tools",
parser,
token,
func=lambda context: context,
Expand Down
4 changes: 3 additions & 1 deletion django/contrib/admin/templatetags/admin_modify.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def prepopulated_fields_js(context):
@register.tag(name="prepopulated_fields_js")
def prepopulated_fields_js_tag(parser, token):
return InclusionAdminNode(
"prepopulated_fields_js",
parser,
token,
func=prepopulated_fields_js,
Expand Down Expand Up @@ -115,14 +116,15 @@ def submit_row(context):
@register.tag(name="submit_row")
def submit_row_tag(parser, token):
return InclusionAdminNode(
parser, token, func=submit_row, template_name="submit_line.html"
"submit_row", parser, token, func=submit_row, template_name="submit_line.html"
)


@register.tag(name="change_form_object_tools")
def change_form_object_tools_tag(parser, token):
"""Display the row of change form object tools."""
return InclusionAdminNode(
"change_form_object_tools",
parser,
token,
func=lambda context: context,
Expand Down
13 changes: 11 additions & 2 deletions django/contrib/admin/templatetags/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from inspect import getfullargspec

from django.template.exceptions import TemplateSyntaxError
from django.template.library import InclusionNode, parse_bits
from django.utils.inspect import lazy_annotations

Expand All @@ -10,12 +11,21 @@ class InclusionAdminNode(InclusionNode):
or globally.
"""

def __init__(self, parser, token, func, template_name, takes_context=True):
def __init__(self, name, parser, token, func, template_name, takes_context=True):
self.template_name = template_name
with lazy_annotations():
params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = (
getfullargspec(func)
)
if takes_context:
if params and params[0] == "context":
del params[0]
else:
function_name = func.__name__
raise TemplateSyntaxError(
f"{name!r} sets takes_context=True so {function_name!r} "
"must have a first argument of 'context'"
)
bits = token.split_contents()
args, kwargs = parse_bits(
parser,
Expand All @@ -26,7 +36,6 @@ def __init__(self, parser, token, func, template_name, takes_context=True):
defaults,
kwonly,
kwonly_defaults,
takes_context,
bits[0],
)
super().__init__(func, takes_context, args, kwargs, filename=None)
Expand Down
12 changes: 8 additions & 4 deletions django/db/models/fields/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2842,10 +2842,14 @@ def validate(self, value, model_instance):
pass

def get_db_prep_value(self, value, connection, prepared=False):
if not prepared:
value = self.get_prep_value(value)
value = connection.ops.validate_autopk_value(value)
return value
return value if prepared else self.get_prep_value(value)

def get_db_prep_save(self, value, connection):
if hasattr(value, "as_sql"):
return value
value = self.get_prep_value(value)
value = connection.ops.validate_autopk_value(value)
return self.get_db_prep_value(value, connection=connection, prepared=True)

def contribute_to_class(self, cls, name, **kwargs):
if cls._meta.auto_field:
Expand Down
68 changes: 40 additions & 28 deletions django/template/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ def dec(func):
) = getfullargspec(unwrap(func))
function_name = name or func.__name__

if takes_context:
if params and params[0] == "context":
del params[0]
else:
raise TemplateSyntaxError(
f"{function_name!r} is decorated with takes_context=True so it "
"must have a first argument of 'context'"
)

@wraps(func)
def compile_func(parser, token):
bits = token.split_contents()[1:]
Expand All @@ -139,7 +148,6 @@ def compile_func(parser, token):
defaults,
kwonly,
kwonly_defaults,
takes_context,
function_name,
)
return SimpleNode(func, takes_context, args, kwargs, target_var)
Expand Down Expand Up @@ -182,26 +190,32 @@ def dec(func):
if end_name is None:
end_name = f"end{function_name}"

@wraps(func)
def compile_func(parser, token):
tag_params = params.copy()
if takes_context:
if len(params) >= 2 and params[1] == "content":
del params[1]
else:
raise TemplateSyntaxError(
f"{function_name!r} is decorated with takes_context=True so"
" it must have a first argument of 'context' and a second "
"argument of 'content'"
)

if takes_context:
if len(tag_params) >= 2 and tag_params[1] == "content":
del tag_params[1]
else:
raise TemplateSyntaxError(
f"{function_name!r} is decorated with takes_context=True so"
" it must have a first argument of 'context' and a second "
"argument of 'content'"
)
elif tag_params and tag_params[0] == "content":
del tag_params[0]
if params and params[0] == "context":
del params[0]
else:
raise TemplateSyntaxError(
f"'{function_name}' must have a first argument of 'content'"
f"{function_name!r} is decorated with takes_context=True so it "
"must have a first argument of 'context'"
)
elif params and params[0] == "content":
del params[0]
else:
raise TemplateSyntaxError(
f"{function_name!r} must have a first argument of 'content'"
)

@wraps(func)
def compile_func(parser, token):
bits = token.split_contents()[1:]
target_var = None
if len(bits) >= 2 and bits[-2] == "as":
Expand All @@ -214,13 +228,12 @@ def compile_func(parser, token):
args, kwargs = parse_bits(
parser,
bits,
tag_params,
params,
varargs,
varkw,
defaults,
kwonly,
kwonly_defaults,
takes_context,
function_name,
)

Expand Down Expand Up @@ -263,6 +276,15 @@ def dec(func):
) = getfullargspec(unwrap(func))
function_name = name or func.__name__

if takes_context:
if params and params[0] == "context":
params = params[1:]
else:
raise TemplateSyntaxError(
f"{function_name!r} is decorated with takes_context=True so it "
"must have a first argument of 'context'"
)

@wraps(func)
def compile_func(parser, token):
bits = token.split_contents()[1:]
Expand All @@ -275,7 +297,6 @@ def compile_func(parser, token):
defaults,
kwonly,
kwonly_defaults,
takes_context,
function_name,
)
return InclusionNode(
Expand Down Expand Up @@ -394,22 +415,13 @@ def parse_bits(
defaults,
kwonly,
kwonly_defaults,
takes_context,
name,
):
"""
Parse bits for template tag helpers simple_tag and inclusion_tag, in
particular by detecting syntax errors and by extracting positional and
keyword arguments.
"""
if takes_context:
if params and params[0] == "context":
params = params[1:]
else:
raise TemplateSyntaxError(
"'%s' is decorated with takes_context=True so it must "
"have a first argument of 'context'" % name
)
args = []
kwargs = {}
unhandled_params = list(params)
Expand Down
4 changes: 2 additions & 2 deletions docs/ref/models/database-functions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1783,10 +1783,10 @@ Usage example:
``StrIndex``
------------

.. class:: StrIndex(string, substring, **extra)
.. class:: StrIndex(expression, substring, **extra)

Returns a positive integer corresponding to the 1-indexed position of the first
occurrence of ``substring`` inside ``string``, or 0 if ``substring`` is not
occurrence of ``substring`` inside ``expression``, or 0 if ``substring`` is not
found.

Usage example:
Expand Down
10 changes: 9 additions & 1 deletion tests/admin_changelist/test_date_hierarchy.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.auth.models import User
from django.test import RequestFactory, TestCase
from django.test import RequestFactory, TestCase, override_settings
from django.urls import reverse
from django.utils.timezone import make_aware

from .admin import EventAdmin
Expand Down Expand Up @@ -95,3 +96,10 @@ def test_invalid_params(self):
self.assertRaises(IncorrectLookupParameters),
):
self.assertDateParams(invalid_query, None, None)

@override_settings(ROOT_URLCONF="admin_changelist.urls")
def test_label_in_hierarchy(self):
self.client.force_login(self.superuser)
Event.objects.create(date=datetime(2017, 1, 1))
response = self.client.get(reverse("admin:admin_changelist_event_changelist"))
self.assertContains(response, "Filter by date", status_code=200)
1 change: 1 addition & 0 deletions tests/admin_views/test_templatetags.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ def action(arg: SomeType = None): # NOQA: F821
# inspect.getfullargspec(), which is not ready for deferred
# evaluation of annotations.
InclusionAdminNode(
"test",
parser=object(),
token=Token(token_type=TokenType.TEXT, contents="a"),
func=action,
Expand Down
Loading