django-unfold 0.46.0__py3-none-any.whl → 0.47.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. {django_unfold-0.46.0.dist-info → django_unfold-0.47.0.dist-info}/METADATA +5 -6
  2. {django_unfold-0.46.0.dist-info → django_unfold-0.47.0.dist-info}/RECORD +38 -31
  3. {django_unfold-0.46.0.dist-info → django_unfold-0.47.0.dist-info}/WHEEL +1 -1
  4. unfold/admin.py +15 -16
  5. unfold/checks.py +4 -4
  6. unfold/components.py +5 -5
  7. unfold/contrib/filters/admin/__init__.py +43 -0
  8. unfold/contrib/filters/admin/autocomplete_filters.py +16 -0
  9. unfold/contrib/filters/admin/datetime_filters.py +212 -0
  10. unfold/contrib/filters/admin/dropdown_filters.py +100 -0
  11. unfold/contrib/filters/admin/mixins.py +146 -0
  12. unfold/contrib/filters/admin/numeric_filters.py +196 -0
  13. unfold/contrib/filters/admin/text_filters.py +65 -0
  14. unfold/contrib/filters/admin.py +32 -32
  15. unfold/contrib/filters/forms.py +68 -17
  16. unfold/contrib/forms/widgets.py +9 -9
  17. unfold/contrib/inlines/checks.py +2 -4
  18. unfold/contrib/simple_history/templates/simple_history/object_history.html +17 -1
  19. unfold/contrib/simple_history/templates/simple_history/object_history_list.html +1 -1
  20. unfold/dataclasses.py +2 -2
  21. unfold/decorators.py +4 -3
  22. unfold/settings.py +2 -2
  23. unfold/sites.py +156 -140
  24. unfold/static/unfold/css/styles.css +1 -1
  25. unfold/static/unfold/js/app.js +2 -2
  26. unfold/templates/admin/filter.html +1 -1
  27. unfold/templates/unfold/helpers/change_list_filter.html +2 -2
  28. unfold/templates/unfold/helpers/change_list_filter_actions.html +1 -1
  29. unfold/templates/unfold/helpers/header_back_button.html +2 -2
  30. unfold/templates/unfold/helpers/tab_list.html +7 -1
  31. unfold/templates/unfold/layouts/skeleton.html +1 -1
  32. unfold/templatetags/unfold.py +55 -22
  33. unfold/templatetags/unfold_list.py +2 -2
  34. unfold/typing.py +5 -4
  35. unfold/utils.py +3 -2
  36. unfold/views.py +2 -2
  37. unfold/widgets.py +27 -27
  38. {django_unfold-0.46.0.dist-info → django_unfold-0.47.0.dist-info}/LICENSE.md +0 -0
@@ -78,9 +78,9 @@ const filterForm = () => {
78
78
  }
79
79
 
80
80
  filterForm.addEventListener("formdata", (event) => {
81
- for (const [key, value] of event.formData.entries()) {
81
+ Array.from(event.formData.entries()).forEach(([key, value]) => {
82
82
  if (value === "") event.formData.delete(key);
83
- }
83
+ });
84
84
  });
85
85
  };
86
86
 
@@ -12,7 +12,7 @@
12
12
  {% endfor %}
13
13
 
14
14
  {% if spec|class_name == "BooleanFieldListFilter" %}
15
- <ul class="flex border min-w-20 rounded shadow-sm text-font-default-light dark:border-base-700 dark:text-font-default-dark w-full">
15
+ <ul class="dark:bg-base-900 border border-base-200 flex min-w-20 rounded shadow-sm text-font-default-light dark:border-base-700 dark:text-font-default-dark w-full">
16
16
  {% for choice in choices %}
17
17
  <li class="basis-1/3 border-r border-base-200 flex-grow truncate last:border-r-0 dark:border-base-700 {% if choice.selected %}font-semibold text-primary-600 dark:text-primary-500 {% else %}hover:text-base-700 dark:hover:text-base-200{% endif %}">
18
18
  <a href="{{ choice.query_string|iriencode }}" title="{{ choice.display }}" class="block px-3 py-2 text-center hover:text-primary-600 dark:hover:text-primary-500">
@@ -10,8 +10,8 @@
10
10
  {% preserve_filters %}
11
11
  {% endif %}
12
12
 
13
- <div class="flex flex-col grow gap-4 overflow-auto *:mb-0" {% if cl.model_admin.list_filter_sheet %}data-simplebar data-simplebar-direction='rtl'{% endif %}>
14
- <div class="flex flex-col gap-4 {% if cl.model_admin.list_filter_sheet %}px-3 py-2.5{% endif %} *:mb-0">
13
+ <div class="flex flex-col grow gap-4 overflow-auto *:mb-0" data-simplebar data-simplebar-direction="rtl">
14
+ <div class="flex flex-col gap-4 mx-1 px-2 py-2.5 {% if not cl.model_admin.list_filter_sheet %} 2xl:px-0 2xl:py-0{% endif %} *:mb-0">
15
15
  {% for spec in cl.filter_specs %}
16
16
  {% admin_list_filter cl spec %}
17
17
  {% endfor %}
@@ -1,6 +1,6 @@
1
1
  {% load i18n %}
2
2
 
3
- <div class="{% if cl.model_admin.list_filter_sheet %}bg-white border-t border-base-200 p-3 py-2.5 dark:bg-base-800 dark:border-base-700{% else %}mt-6{% endif %}">
3
+ <div class="bg-white border-t border-base-200 p-3 py-2.5 dark:bg-base-800 dark:border-base-700{% if not cl.model_admin.list_filter_sheet %} 2xl:!border-t-0 2xl:!bg-transparent 2xl:px-0{% endif %}">
4
4
  {% if cl.model_admin.list_filter_submit %}
5
5
  <button type="submit" class="bg-primary-600 block border border-transparent font-medium px-3 py-2 rounded text-white w-full">
6
6
  {% trans "Apply Filters" %}
@@ -2,9 +2,9 @@
2
2
 
3
3
  {% if show_back_button %}
4
4
  {% if opts and adminform %}
5
- {% url opts|admin_urlname:'changelist' as link %}
5
+ {% url opts|admin_urlname:'changelist' as changelist_url %}
6
6
 
7
- <a href="{{ link }}" class="block h-4.5 mr-3" title="{% trans "Go back" %}">
7
+ <a href="{% add_preserved_filters changelist_url %}" class="block h-4.5 mr-3" title="{% trans "Go back" %}">
8
8
  <span class="material-symbols-outlined">
9
9
  arrow_back
10
10
  </span>
@@ -8,7 +8,13 @@
8
8
  {% for item in tabs_list %}
9
9
  {% if item.has_permission %}
10
10
  <li class="border-b last:border-b-0 md:border-b-0 md:mr-8 dark:border-base-800">
11
- <a href="{% if item.link_callback %}{{ item.link_callback }}{% else %}{{ item.link }}{% endif %}" class="block px-3 py-2 md:py-4 md:px-0 dark:border-base-800 {% if item.active %} border-b font-semibold -mb-px text-primary-600 hover:text-primary-600 dark:text-primary-500 dark:hover:text-primary-500 md:border-primary-500 dark:md:!border-primary-600{% else %}font-medium hover:text-primary-600 dark:hover:text-primary-500{% endif %}">
11
+ <a href="{% if item.link_callback %}{{ item.link_callback }}{% else %}{{ item.link }}{% endif %}"
12
+ class="block px-3 py-2 md:py-4 md:px-0 dark:border-base-800 {% if item.active and not item.inline %} border-b font-semibold -mb-px text-primary-600 hover:text-primary-600 dark:text-primary-500 dark:hover:text-primary-500 md:border-primary-500 dark:md:!border-primary-600{% else %}font-medium hover:text-primary-600 dark:hover:text-primary-500{% endif %}"
13
+ {% if item.inline %}
14
+ x-on:click="activeTab = '{{ item.inline }}'"
15
+ x-bind:class="{'border-b border-base-200 dark:border-base-800 md:border-primary-500 dark:md:!border-primary-600 font-semibold -mb-px text-primary-600 dark:text-primary-500': activeTab == '{{ item.inline }}'}"
16
+ {% endif %}
17
+ >
12
18
  {{ item.title }}
13
19
  </a>
14
20
  </li>
@@ -59,7 +59,7 @@
59
59
  {% endblock %}
60
60
  </head>
61
61
 
62
- <body class="antialiased bg-white font-sans text-font-default-light text-sm dark:bg-base-900 dark:text-font-default-dark {% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}" data-admin-utc-offset="{% now "Z" %}" x-data="{ mainWidth: 0, activeTab: 'general', sidebarMobileOpen: false, sidebarDesktopOpen: {% if request.session.toggle_sidebar == False %}false{% else %}true{% endif %} }" x-init="activeTab = window.location.hash?.replace('#', '') || 'general'">
62
+ <body class="antialiased bg-white font-sans text-font-default-light text-sm dark:bg-base-900 dark:text-font-default-dark {% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}" data-admin-utc-offset="{% now "Z" %}" x-data="{ mainWidth: 0, {% if opts %}activeTab: 'general',{% endif %} sidebarMobileOpen: false, sidebarDesktopOpen: {% if request.session.toggle_sidebar == False %}false{% else %}true{% endif %} }" x-init="activeTab = {% if opts %}window.location.hash?.replace('#', '') || 'general'{% else %}''{% endif %}">
63
63
  {% if colors %}
64
64
  <style id="unfold-theme-colors">
65
65
  :root {
@@ -1,8 +1,10 @@
1
- from typing import Any, Dict, List, Mapping, Optional, Set, Union
1
+ from collections.abc import Mapping
2
+ from typing import Any, Optional, Union
2
3
 
3
4
  from django import template
4
5
  from django.contrib.admin.helpers import AdminForm, Fieldset
5
6
  from django.contrib.admin.views.main import ChangeList
7
+ from django.db.models.options import Options
6
8
  from django.forms import Field
7
9
  from django.http import HttpRequest
8
10
  from django.template import Context, Library, Node, RequestContext, TemplateSyntaxError
@@ -15,9 +17,44 @@ from unfold.components import ComponentRegistry
15
17
  register = Library()
16
18
 
17
19
 
18
- @register.simple_tag(name="tab_list", takes_context=True)
19
- def tab_list(context, page, opts) -> str:
20
+ def _get_tabs_list(
21
+ context: RequestContext, page: str, opts: Optional[Options] = None
22
+ ) -> list:
20
23
  tabs_list = []
24
+ page_id = None
25
+
26
+ if page not in ["changeform", "changelist"]:
27
+ page_id = page
28
+
29
+ for tab in context.get("tab_list", []):
30
+ if page_id:
31
+ if tab.get("page") == page_id:
32
+ tabs_list = tab["items"]
33
+ break
34
+
35
+ continue
36
+
37
+ if "models" not in tab:
38
+ continue
39
+
40
+ for tab_model in tab["models"]:
41
+ if isinstance(tab_model, str):
42
+ if str(opts) == tab_model and page == "changelist":
43
+ tabs_list = tab["items"]
44
+ break
45
+ elif isinstance(tab_model, dict) and str(opts) == tab_model["name"]:
46
+ is_detail = tab_model.get("detail", False)
47
+
48
+ if (page == "changeform" and is_detail) or (
49
+ page == "changelist" and not is_detail
50
+ ):
51
+ tabs_list = tab["items"]
52
+ break
53
+ return tabs_list
54
+
55
+
56
+ @register.simple_tag(name="tab_list", takes_context=True)
57
+ def tab_list(context: RequestContext, page: str, opts: Optional[Options] = None) -> str:
21
58
  inlines_list = []
22
59
 
23
60
  data = {
@@ -26,22 +63,18 @@ def tab_list(context, page, opts) -> str:
26
63
  "actions_list": context.get("actions_list"),
27
64
  "actions_items": context.get("actions_items"),
28
65
  "is_popup": context.get("is_popup"),
66
+ "tabs_list": _get_tabs_list(context, page, opts),
29
67
  }
30
68
 
31
- for tab in context.get("tab_list", []):
32
- if str(opts) in tab["models"]:
33
- tabs_list = tab["items"]
34
- break
35
-
36
- if page == "changelist":
37
- data["tabs_list"] = tabs_list
38
-
39
- for inline in context.get("inline_admin_formsets", []):
40
- if hasattr(inline.opts, "tab"):
41
- inlines_list.append(inline)
69
+ # If the changeform is rendered and there are no custom tab navigation
70
+ # specified, check for inlines to put into tabs
71
+ if page == "changeform" and len(data["tabs_list"]) == 0:
72
+ for inline in context.get("inline_admin_formsets", []):
73
+ if opts and hasattr(inline.opts, "tab"):
74
+ inlines_list.append(inline)
42
75
 
43
- if page == "changeform" and len(inlines_list) > 0:
44
- data["inlines_list"] = inlines_list
76
+ if len(inlines_list) > 0:
77
+ data["inlines_list"] = inlines_list
45
78
 
46
79
  return render_to_string(
47
80
  "unfold/helpers/tab_list.html",
@@ -70,7 +103,7 @@ def index(indexable: Mapping[int, Any], i: int) -> Any:
70
103
 
71
104
 
72
105
  @register.filter
73
- def tabs(adminform: AdminForm) -> List[Fieldset]:
106
+ def tabs(adminform: AdminForm) -> list[Fieldset]:
74
107
  result = []
75
108
 
76
109
  for fieldset in adminform:
@@ -86,7 +119,7 @@ class CaptureNode(Node):
86
119
  self.varname = varname
87
120
  self.silent = silent
88
121
 
89
- def render(self, context: Dict[str, Any]) -> Union[str, SafeText]:
122
+ def render(self, context: dict[str, Any]) -> Union[str, SafeText]:
90
123
  output = self.nodelist.render(context)
91
124
  context[self.varname] = output
92
125
  if self.silent:
@@ -155,7 +188,7 @@ class RenderComponentNode(template.Node):
155
188
  self,
156
189
  template_name: str,
157
190
  nodelist: NodeList,
158
- extra_context: Optional[Dict] = None,
191
+ extra_context: Optional[dict] = None,
159
192
  include_context: bool = False,
160
193
  *args,
161
194
  **kwargs,
@@ -252,7 +285,7 @@ def add_css_class(field: Field, classes: Union[list, tuple]) -> Field:
252
285
  takes_context=True,
253
286
  name="preserve_filters",
254
287
  )
255
- def preserve_changelist_filters(context: Context) -> Dict[str, Dict[str, str]]:
288
+ def preserve_changelist_filters(context: Context) -> dict[str, dict[str, str]]:
256
289
  """
257
290
  Generate hidden input fields to preserve filters for POST forms.
258
291
  """
@@ -262,10 +295,10 @@ def preserve_changelist_filters(context: Context) -> Dict[str, Dict[str, str]]:
262
295
  if not request or not changelist:
263
296
  return {"params": {}}
264
297
 
265
- used_params: Set[str] = {
298
+ used_params: set[str] = {
266
299
  param for spec in changelist.filter_specs for param in spec.used_parameters
267
300
  }
268
- preserved_params: Dict[str, str] = {
301
+ preserved_params: dict[str, str] = {
269
302
  param: value for param, value in request.GET.items() if param not in used_params
270
303
  }
271
304
 
@@ -1,5 +1,5 @@
1
1
  import datetime
2
- from typing import Any, Dict, Optional, Union
2
+ from typing import Any, Optional, Union
3
3
 
4
4
  from django.contrib.admin.templatetags.admin_list import (
5
5
  ResultList,
@@ -355,7 +355,7 @@ def results(cl: ChangeList):
355
355
  yield UnfoldResultList(pk_value, None, items_for_result(cl, res, None))
356
356
 
357
357
 
358
- def result_list(context: Dict[str, Any], cl: ChangeList) -> Dict[str, Any]:
358
+ def result_list(context: dict[str, Any], cl: ChangeList) -> dict[str, Any]:
359
359
  """
360
360
  Display the headers and data list together.
361
361
  """
unfold/typing.py CHANGED
@@ -1,4 +1,5 @@
1
- from typing import Any, Dict, Iterable, List, Protocol, Tuple, Union
1
+ from collections.abc import Iterable
2
+ from typing import Any, Protocol, Union
2
3
 
3
4
 
4
5
  class ActionFunction(Protocol):
@@ -11,13 +12,13 @@ class ActionFunction(Protocol):
11
12
  allowed_permissions: Iterable[str]
12
13
  short_description: str
13
14
  url_path: str
14
- attrs: Dict[str, Any]
15
+ attrs: dict[str, Any]
15
16
 
16
17
  def __call__(self, *args, **kwargs):
17
18
  pass
18
19
 
19
20
 
20
21
  FieldsetsType = Union[
21
- List[Tuple[Union[str, None], Dict[str, Any]]],
22
- Tuple[Tuple[Union[str, None], Dict[str, Any]]],
22
+ list[tuple[Union[str, None], dict[str, Any]]],
23
+ tuple[tuple[Union[str, None], dict[str, Any]]],
23
24
  ]
unfold/utils.py CHANGED
@@ -1,7 +1,8 @@
1
1
  import datetime
2
2
  import decimal
3
3
  import json
4
- from typing import Any, Iterable, List, Optional
4
+ from collections.abc import Iterable
5
+ from typing import Any, Optional
5
6
 
6
7
  from django.conf import settings
7
8
  from django.db import models
@@ -114,7 +115,7 @@ def display_for_field(value: Any, field: Any, empty_value_display: str) -> str:
114
115
  return display_for_value(value, empty_value_display)
115
116
 
116
117
 
117
- def hex_to_rgb(hex_color: str) -> List[int]:
118
+ def hex_to_rgb(hex_color: str) -> list[int]:
118
119
  hex_color = hex_color.lstrip("#")
119
120
 
120
121
  r = int(hex_color[0:2], 16)
unfold/views.py CHANGED
@@ -1,4 +1,4 @@
1
- from typing import Any, Dict
1
+ from typing import Any
2
2
 
3
3
  from django.contrib.auth.mixins import PermissionRequiredMixin
4
4
 
@@ -16,7 +16,7 @@ class UnfoldModelAdminViewMixin(PermissionRequiredMixin):
16
16
  self.model_admin = model_admin
17
17
  super().__init__(**kwargs)
18
18
 
19
- def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
19
+ def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
20
20
  if not hasattr(self, "model_admin"):
21
21
  raise UnfoldException(
22
22
  "UnfoldModelAdminViewMixin was not provided with 'model_admin' argument"
unfold/widgets.py CHANGED
@@ -1,4 +1,4 @@
1
- from typing import Any, Callable, Dict, Optional, Tuple, Union
1
+ from typing import Any, Callable, Optional, Union
2
2
 
3
3
  from django.contrib.admin.options import VERTICAL
4
4
  from django.contrib.admin.sites import AdminSite
@@ -255,7 +255,7 @@ SWITCH_CLASSES = [
255
255
 
256
256
 
257
257
  class UnfoldAdminTextInputWidget(AdminTextInputWidget):
258
- def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
258
+ def __init__(self, attrs: Optional[dict[str, Any]] = None) -> None:
259
259
  super().__init__(
260
260
  attrs={
261
261
  **(attrs or {}),
@@ -269,7 +269,7 @@ class UnfoldAdminTextInputWidget(AdminTextInputWidget):
269
269
  class UnfoldAdminURLInputWidget(AdminURLFieldWidget):
270
270
  template_name = "unfold/widgets/url.html"
271
271
 
272
- def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
272
+ def __init__(self, attrs: Optional[dict[str, Any]] = None) -> None:
273
273
  super().__init__(
274
274
  attrs={
275
275
  **(attrs or {}),
@@ -281,7 +281,7 @@ class UnfoldAdminURLInputWidget(AdminURLFieldWidget):
281
281
 
282
282
 
283
283
  class UnfoldAdminColorInputWidget(AdminTextInputWidget):
284
- def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
284
+ def __init__(self, attrs: Optional[dict[str, Any]] = None) -> None:
285
285
  super().__init__(
286
286
  attrs={
287
287
  **(attrs or {}),
@@ -294,7 +294,7 @@ class UnfoldAdminColorInputWidget(AdminTextInputWidget):
294
294
 
295
295
 
296
296
  class UnfoldAdminUUIDInputWidget(AdminUUIDInputWidget):
297
- def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
297
+ def __init__(self, attrs: Optional[dict[str, Any]] = None) -> None:
298
298
  super().__init__(
299
299
  attrs={
300
300
  **(attrs or {}),
@@ -308,7 +308,7 @@ class UnfoldAdminUUIDInputWidget(AdminUUIDInputWidget):
308
308
  class UnfoldAdminIntegerRangeWidget(MultiWidget):
309
309
  template_name = "unfold/widgets/range.html"
310
310
 
311
- def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
311
+ def __init__(self, attrs: Optional[dict[str, Any]] = None) -> None:
312
312
  if attrs is None:
313
313
  attrs = {}
314
314
 
@@ -320,14 +320,14 @@ class UnfoldAdminIntegerRangeWidget(MultiWidget):
320
320
 
321
321
  super().__init__(_widgets, attrs)
322
322
 
323
- def decompress(self, value: Union[str, None]) -> Tuple[Optional[Callable], ...]:
323
+ def decompress(self, value: Union[str, None]) -> tuple[Optional[Callable], ...]:
324
324
  if value:
325
325
  return value.lower, value.upper
326
326
  return None, None
327
327
 
328
328
 
329
329
  class UnfoldAdminEmailInputWidget(AdminEmailInputWidget):
330
- def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
330
+ def __init__(self, attrs: Optional[dict[str, Any]] = None) -> None:
331
331
  super().__init__(
332
332
  attrs={
333
333
  **(attrs or {}),
@@ -374,7 +374,7 @@ class UnfoldAdminDateWidget(AdminDateWidget):
374
374
  template_name = "unfold/widgets/date.html"
375
375
 
376
376
  def __init__(
377
- self, attrs: Optional[Dict[str, Any]] = None, format: Optional[str] = None
377
+ self, attrs: Optional[dict[str, Any]] = None, format: Optional[str] = None
378
378
  ) -> None:
379
379
  attrs = {
380
380
  **(attrs or {}),
@@ -394,7 +394,7 @@ class UnfoldAdminSingleDateWidget(AdminDateWidget):
394
394
  template_name = "unfold/widgets/date.html"
395
395
 
396
396
  def __init__(
397
- self, attrs: Optional[Dict[str, Any]] = None, format: Optional[str] = None
397
+ self, attrs: Optional[dict[str, Any]] = None, format: Optional[str] = None
398
398
  ) -> None:
399
399
  attrs = {
400
400
  **(attrs or {}),
@@ -414,7 +414,7 @@ class UnfoldAdminTimeWidget(AdminTimeWidget):
414
414
  template_name = "unfold/widgets/time.html"
415
415
 
416
416
  def __init__(
417
- self, attrs: Optional[Dict[str, Any]] = None, format: Optional[str] = None
417
+ self, attrs: Optional[dict[str, Any]] = None, format: Optional[str] = None
418
418
  ) -> None:
419
419
  attrs = {
420
420
  **(attrs or {}),
@@ -434,7 +434,7 @@ class UnfoldAdminSingleTimeWidget(AdminTimeWidget):
434
434
  template_name = "unfold/widgets/time.html"
435
435
 
436
436
  def __init__(
437
- self, attrs: Optional[Dict[str, Any]] = None, format: Optional[str] = None
437
+ self, attrs: Optional[dict[str, Any]] = None, format: Optional[str] = None
438
438
  ) -> None:
439
439
  attrs = {
440
440
  **(attrs or {}),
@@ -453,7 +453,7 @@ class UnfoldAdminSingleTimeWidget(AdminTimeWidget):
453
453
  class UnfoldAdminTextareaWidget(AdminTextareaWidget):
454
454
  template_name = "unfold/widgets/textarea.html"
455
455
 
456
- def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
456
+ def __init__(self, attrs: Optional[dict[str, Any]] = None) -> None:
457
457
  attrs = attrs or {}
458
458
 
459
459
  super().__init__(
@@ -473,7 +473,7 @@ class UnfoldAdminTextareaWidget(AdminTextareaWidget):
473
473
  class UnfoldAdminExpandableTextareaWidget(AdminTextareaWidget):
474
474
  template_name = "unfold/widgets/textarea_expandable.html"
475
475
 
476
- def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
476
+ def __init__(self, attrs: Optional[dict[str, Any]] = None) -> None:
477
477
  attrs = attrs or {}
478
478
 
479
479
  attrs.update({"rows": 2})
@@ -496,7 +496,7 @@ class UnfoldAdminExpandableTextareaWidget(AdminTextareaWidget):
496
496
  class UnfoldAdminSplitDateTimeWidget(AdminSplitDateTime):
497
497
  template_name = "unfold/widgets/split_datetime.html"
498
498
 
499
- def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
499
+ def __init__(self, attrs: Optional[dict[str, Any]] = None) -> None:
500
500
  widgets = [UnfoldAdminDateWidget, UnfoldAdminTimeWidget]
501
501
  MultiWidget.__init__(self, widgets, attrs)
502
502
 
@@ -506,9 +506,9 @@ class UnfoldAdminSplitDateTimeVerticalWidget(AdminSplitDateTime):
506
506
 
507
507
  def __init__(
508
508
  self,
509
- attrs: Optional[Dict[str, Any]] = None,
510
- date_attrs: Optional[Dict[str, Any]] = None,
511
- time_attrs: Optional[Dict[str, Any]] = None,
509
+ attrs: Optional[dict[str, Any]] = None,
510
+ date_attrs: Optional[dict[str, Any]] = None,
511
+ time_attrs: Optional[dict[str, Any]] = None,
512
512
  date_label: Optional[str] = None,
513
513
  time_label: Optional[str] = None,
514
514
  ) -> None:
@@ -522,8 +522,8 @@ class UnfoldAdminSplitDateTimeVerticalWidget(AdminSplitDateTime):
522
522
  MultiWidget.__init__(self, widgets, attrs)
523
523
 
524
524
  def get_context(
525
- self, name: str, value: Any, attrs: Optional[Dict[str, Any]]
526
- ) -> Dict[str, Any]:
525
+ self, name: str, value: Any, attrs: Optional[dict[str, Any]]
526
+ ) -> dict[str, Any]:
527
527
  context = super().get_context(name, value, attrs)
528
528
 
529
529
  if self.date_label is not None:
@@ -540,7 +540,7 @@ class UnfoldAdminSplitDateTimeVerticalWidget(AdminSplitDateTime):
540
540
 
541
541
 
542
542
  class UnfoldAdminIntegerFieldWidget(AdminIntegerFieldWidget):
543
- def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
543
+ def __init__(self, attrs: Optional[dict[str, Any]] = None) -> None:
544
544
  super().__init__(
545
545
  attrs={
546
546
  **(attrs or {}),
@@ -552,7 +552,7 @@ class UnfoldAdminIntegerFieldWidget(AdminIntegerFieldWidget):
552
552
 
553
553
 
554
554
  class UnfoldAdminDecimalFieldWidget(AdminIntegerFieldWidget):
555
- def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
555
+ def __init__(self, attrs: Optional[dict[str, Any]] = None) -> None:
556
556
  super().__init__(
557
557
  attrs={
558
558
  **(attrs or {}),
@@ -564,7 +564,7 @@ class UnfoldAdminDecimalFieldWidget(AdminIntegerFieldWidget):
564
564
 
565
565
 
566
566
  class UnfoldAdminBigIntegerFieldWidget(AdminBigIntegerFieldWidget):
567
- def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
567
+ def __init__(self, attrs: Optional[dict[str, Any]] = None) -> None:
568
568
  super().__init__(
569
569
  attrs={
570
570
  **(attrs or {}),
@@ -621,7 +621,7 @@ class UnfoldAdminRadioSelectWidget(AdminRadioSelect):
621
621
  self.radio_style = radio_style
622
622
  self.attrs["class"] = " ".join([*RADIO_CLASSES, self.attrs.get("class", "")])
623
623
 
624
- def get_context(self, *args, **kwargs) -> Dict[str, Any]:
624
+ def get_context(self, *args, **kwargs) -> dict[str, Any]:
625
625
  context = super().get_context(*args, **kwargs)
626
626
  context.update({"radio_style": self.radio_style})
627
627
  return context
@@ -666,7 +666,7 @@ except ImportError:
666
666
 
667
667
  class UnfoldBooleanWidget(CheckboxInput):
668
668
  def __init__(
669
- self, attrs: Optional[Dict[str, Any]] = None, check_test: Callable = None
669
+ self, attrs: Optional[dict[str, Any]] = None, check_test: Callable = None
670
670
  ) -> None:
671
671
  if attrs is None:
672
672
  attrs = {}
@@ -684,7 +684,7 @@ class UnfoldBooleanWidget(CheckboxInput):
684
684
 
685
685
  class UnfoldBooleanSwitchWidget(CheckboxInput):
686
686
  def __init__(
687
- self, attrs: Optional[Dict[str, Any]] = None, check_test: Callable = None
687
+ self, attrs: Optional[dict[str, Any]] = None, check_test: Callable = None
688
688
  ) -> None:
689
689
  super().__init__(
690
690
  attrs={
@@ -708,7 +708,7 @@ class UnfoldForeignKeyRawIdWidget(ForeignKeyRawIdWidget):
708
708
  self,
709
709
  rel: ForeignObjectRel,
710
710
  admin_site: AdminSite,
711
- attrs: Optional[Dict] = None,
711
+ attrs: Optional[dict] = None,
712
712
  using: Optional[Any] = None,
713
713
  ) -> None:
714
714
  attrs = {