django-unfold 0.67.0__py3-none-any.whl → 0.68.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.
- {django_unfold-0.67.0.dist-info → django_unfold-0.68.0.dist-info}/METADATA +8 -5
- {django_unfold-0.67.0.dist-info → django_unfold-0.68.0.dist-info}/RECORD +39 -36
- unfold/admin.py +73 -13
- unfold/components.py +2 -2
- unfold/contrib/filters/admin/choice_filters.py +13 -1
- unfold/contrib/filters/admin/mixins.py +3 -3
- unfold/contrib/filters/admin/numeric_filters.py +6 -6
- unfold/contrib/forms/widgets.py +5 -5
- unfold/contrib/inlines/admin.py +3 -3
- unfold/contrib/inlines/forms.py +5 -4
- unfold/dataclasses.py +13 -13
- unfold/datasets.py +69 -0
- unfold/decorators.py +19 -19
- unfold/fields.py +3 -5
- unfold/forms.py +19 -7
- unfold/mixins/action_model_admin.py +11 -10
- unfold/mixins/base_model_admin.py +6 -6
- unfold/sites.py +14 -17
- unfold/static/unfold/css/styles.css +1 -1
- unfold/static/unfold/js/app.js +3 -1
- unfold/styles.css +21 -16
- unfold/templates/admin/change_form.html +5 -1
- unfold/templates/admin/change_list_results.html +10 -62
- unfold/templates/admin/edit_inline/stacked.html +1 -1
- unfold/templates/admin/search_form.html +5 -3
- unfold/templates/unfold/helpers/change_list_headers.html +65 -0
- unfold/templates/unfold/helpers/dataset.html +19 -0
- unfold/templates/unfold/helpers/edit_inline/tabular_field.html +1 -1
- unfold/templates/unfold/helpers/empty_results.html +6 -4
- unfold/templates/unfold/helpers/field_readonly_value_file.html +1 -1
- unfold/templates/unfold/helpers/tab_items.html +6 -0
- unfold/templatetags/unfold.py +18 -13
- unfold/templatetags/unfold_list.py +64 -8
- unfold/typing.py +5 -6
- unfold/utils.py +9 -9
- unfold/views.py +15 -1
- unfold/widgets.py +30 -29
- {django_unfold-0.67.0.dist-info → django_unfold-0.68.0.dist-info}/WHEEL +0 -0
- {django_unfold-0.67.0.dist-info → django_unfold-0.68.0.dist-info}/licenses/LICENSE.md +0 -0
unfold/contrib/inlines/forms.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
from
|
1
|
+
from collections.abc import Callable
|
2
|
+
from typing import Any
|
2
3
|
|
3
4
|
from django.db.models import Model, QuerySet
|
4
5
|
from django.forms import BaseModelFormSet, ModelForm, modelformset_factory
|
@@ -9,7 +10,7 @@ from unfold.forms import PaginationFormSetMixin
|
|
9
10
|
class NonrelatedInlineModelFormSet(PaginationFormSetMixin, BaseModelFormSet):
|
10
11
|
def __init__(
|
11
12
|
self,
|
12
|
-
instance:
|
13
|
+
instance: Model | None = None,
|
13
14
|
save_as_new: bool = False,
|
14
15
|
**kwargs: Any,
|
15
16
|
) -> None:
|
@@ -33,9 +34,9 @@ class NonrelatedInlineModelFormSet(PaginationFormSetMixin, BaseModelFormSet):
|
|
33
34
|
|
34
35
|
def nonrelated_inline_formset_factory(
|
35
36
|
model: Model,
|
36
|
-
queryset:
|
37
|
+
queryset: QuerySet | None = None,
|
37
38
|
formset: BaseModelFormSet = NonrelatedInlineModelFormSet,
|
38
|
-
save_new_instance:
|
39
|
+
save_new_instance: Callable | None = None,
|
39
40
|
**kwargs: Any,
|
40
41
|
) -> BaseModelFormSet:
|
41
42
|
inline_formset = modelformset_factory(model, formset=formset, **kwargs)
|
unfold/dataclasses.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
+
from collections.abc import Callable
|
1
2
|
from dataclasses import dataclass
|
2
|
-
from typing import Callable, Optional, Union
|
3
3
|
|
4
4
|
from unfold.enums import ActionVariant
|
5
5
|
|
@@ -12,10 +12,10 @@ class UnfoldAction:
|
|
12
12
|
method: ActionFunction
|
13
13
|
description: str
|
14
14
|
path: str
|
15
|
-
attrs:
|
16
|
-
object_id:
|
17
|
-
icon:
|
18
|
-
variant:
|
15
|
+
attrs: dict | None = None
|
16
|
+
object_id: int | str | None = None
|
17
|
+
icon: str | None = None
|
18
|
+
variant: ActionVariant | None = ActionVariant.DEFAULT
|
19
19
|
|
20
20
|
|
21
21
|
@dataclass
|
@@ -23,20 +23,20 @@ class SearchResult:
|
|
23
23
|
title: str
|
24
24
|
description: str
|
25
25
|
link: str
|
26
|
-
icon:
|
26
|
+
icon: str | None
|
27
27
|
|
28
28
|
|
29
29
|
@dataclass
|
30
30
|
class Favicon:
|
31
|
-
href:
|
32
|
-
rel:
|
33
|
-
type:
|
34
|
-
sizes:
|
31
|
+
href: str | Callable
|
32
|
+
rel: str | None = None
|
33
|
+
type: str | None = None
|
34
|
+
sizes: str | None = None
|
35
35
|
|
36
36
|
|
37
37
|
@dataclass
|
38
38
|
class DropdownItem:
|
39
39
|
title: str
|
40
|
-
link:
|
41
|
-
icon:
|
42
|
-
attrs:
|
40
|
+
link: str | Callable
|
41
|
+
icon: str | None = None
|
42
|
+
attrs: dict | None = None
|
unfold/datasets.py
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
from django.contrib import admin
|
4
|
+
from django.http import HttpRequest
|
5
|
+
from django.template.loader import render_to_string
|
6
|
+
|
7
|
+
from unfold.views import DatasetChangeList
|
8
|
+
|
9
|
+
|
10
|
+
class BaseDataset:
|
11
|
+
tab = False
|
12
|
+
|
13
|
+
def __init__(
|
14
|
+
self, request: HttpRequest, extra_context: dict[str, Any] | None
|
15
|
+
) -> None:
|
16
|
+
self.request = request
|
17
|
+
self.extra_context = extra_context
|
18
|
+
|
19
|
+
self.model_admin_instance = self.model_admin(
|
20
|
+
model=self.model, admin_site=admin.site
|
21
|
+
)
|
22
|
+
self.model_admin_instance.extra_context = self.extra_context
|
23
|
+
|
24
|
+
@property
|
25
|
+
def contents(self) -> str:
|
26
|
+
return render_to_string(
|
27
|
+
"unfold/helpers/dataset.html",
|
28
|
+
request=self.request,
|
29
|
+
context={
|
30
|
+
"dataset": self,
|
31
|
+
"cl": self.cl(),
|
32
|
+
"opts": self.model._meta,
|
33
|
+
},
|
34
|
+
)
|
35
|
+
|
36
|
+
def cl(self) -> DatasetChangeList:
|
37
|
+
list_display = self.model_admin_instance.get_list_display(self.request)
|
38
|
+
list_display_links = self.model_admin_instance.get_list_display_links(
|
39
|
+
self.request, list_display
|
40
|
+
)
|
41
|
+
sortable_by = self.model_admin_instance.get_sortable_by(self.request)
|
42
|
+
search_fields = self.model_admin_instance.get_search_fields(self.request)
|
43
|
+
cl = DatasetChangeList(
|
44
|
+
request=self.request,
|
45
|
+
model=self.model,
|
46
|
+
model_admin=self.model_admin_instance,
|
47
|
+
list_display=list_display,
|
48
|
+
list_display_links=list_display_links,
|
49
|
+
list_filter=[],
|
50
|
+
date_hierarchy=[],
|
51
|
+
search_fields=search_fields,
|
52
|
+
list_select_related=[],
|
53
|
+
list_per_page=10,
|
54
|
+
list_max_show_all=False,
|
55
|
+
list_editable=[],
|
56
|
+
sortable_by=sortable_by,
|
57
|
+
search_help_text=[],
|
58
|
+
)
|
59
|
+
cl.formset = None
|
60
|
+
|
61
|
+
return cl
|
62
|
+
|
63
|
+
@property
|
64
|
+
def model_name(self) -> str:
|
65
|
+
return self.model._meta.model_name
|
66
|
+
|
67
|
+
@property
|
68
|
+
def model_verbose_name(self) -> str:
|
69
|
+
return self.model._meta.verbose_name_plural
|
unfold/decorators.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
from collections.abc import Iterable
|
2
|
-
from typing import Any
|
1
|
+
from collections.abc import Callable, Iterable
|
2
|
+
from typing import Any
|
3
3
|
|
4
4
|
from django.contrib.admin.options import BaseModelAdmin
|
5
5
|
from django.core.exceptions import PermissionDenied
|
@@ -12,14 +12,14 @@ from unfold.typing import ActionFunction
|
|
12
12
|
|
13
13
|
|
14
14
|
def action(
|
15
|
-
function:
|
15
|
+
function: Callable | None = None,
|
16
16
|
*,
|
17
|
-
permissions:
|
18
|
-
description:
|
19
|
-
url_path:
|
20
|
-
attrs:
|
21
|
-
icon:
|
22
|
-
variant:
|
17
|
+
permissions: Iterable[str] | None = None,
|
18
|
+
description: str | None = None,
|
19
|
+
url_path: str | None = None,
|
20
|
+
attrs: dict[str, Any] | None = None,
|
21
|
+
icon: str | None = None,
|
22
|
+
variant: ActionVariant | None = ActionVariant.DEFAULT,
|
23
23
|
) -> ActionFunction:
|
24
24
|
def decorator(func: Callable) -> ActionFunction:
|
25
25
|
def inner(
|
@@ -27,7 +27,7 @@ def action(
|
|
27
27
|
request: HttpRequest,
|
28
28
|
*args: Any,
|
29
29
|
**kwargs,
|
30
|
-
) ->
|
30
|
+
) -> HttpResponse | None:
|
31
31
|
if permissions:
|
32
32
|
permission_rules = []
|
33
33
|
|
@@ -96,16 +96,16 @@ def action(
|
|
96
96
|
|
97
97
|
|
98
98
|
def display(
|
99
|
-
function:
|
99
|
+
function: Callable[[Model], Any] | None = None,
|
100
100
|
*,
|
101
|
-
boolean:
|
102
|
-
image:
|
103
|
-
ordering:
|
104
|
-
description:
|
105
|
-
empty_value:
|
106
|
-
dropdown:
|
107
|
-
label:
|
108
|
-
header:
|
101
|
+
boolean: bool | None = None,
|
102
|
+
image: bool | None = None,
|
103
|
+
ordering: str | Combinable | BaseExpression | None = None,
|
104
|
+
description: str | None = None,
|
105
|
+
empty_value: str | None = None,
|
106
|
+
dropdown: bool | None = None,
|
107
|
+
label: bool | str | dict[str, str] | None = None,
|
108
|
+
header: bool | None = None,
|
109
109
|
) -> Callable:
|
110
110
|
def decorator(func: Callable[[Model], Any]) -> Callable:
|
111
111
|
if boolean is not None and empty_value is not None:
|
unfold/fields.py
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
from typing import Union
|
2
|
-
|
3
1
|
from django.contrib.admin import helpers
|
4
2
|
from django.contrib.admin.utils import lookup_field, quote
|
5
3
|
from django.core.exceptions import ObjectDoesNotExist
|
@@ -40,7 +38,7 @@ class UnfoldAdminReadonlyField(helpers.AdminReadonlyField):
|
|
40
38
|
return format_html("<label{}>{}</label>", flatatt(attrs), capfirst(label))
|
41
39
|
|
42
40
|
@property
|
43
|
-
def url(self) ->
|
41
|
+
def url(self) -> str | bool:
|
44
42
|
field, obj, model_admin = (
|
45
43
|
self.field["field"],
|
46
44
|
self.form.instance,
|
@@ -112,7 +110,7 @@ class UnfoldAdminReadonlyField(helpers.AdminReadonlyField):
|
|
112
110
|
except (AttributeError, ValueError, ObjectDoesNotExist):
|
113
111
|
return False
|
114
112
|
|
115
|
-
return isinstance(f,
|
113
|
+
return isinstance(f, ImageField | FileField)
|
116
114
|
|
117
115
|
def contents(self) -> str:
|
118
116
|
contents = self._get_contents()
|
@@ -167,7 +165,7 @@ class UnfoldAdminReadonlyField(helpers.AdminReadonlyField):
|
|
167
165
|
if isinstance(f.remote_field, ManyToManyRel) and value is not None:
|
168
166
|
result_repr = ", ".join(map(str, value.all()))
|
169
167
|
elif (
|
170
|
-
isinstance(f.remote_field,
|
168
|
+
isinstance(f.remote_field, ForeignObjectRel | OneToOneField)
|
171
169
|
and value is not None
|
172
170
|
):
|
173
171
|
result_repr = self.get_admin_url(f.remote_field, value)
|
unfold/forms.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from collections.abc import Generator
|
2
|
-
from typing import
|
2
|
+
from typing import Any, Union
|
3
3
|
|
4
4
|
from django import forms
|
5
5
|
from django.contrib.admin.forms import (
|
@@ -8,6 +8,7 @@ from django.contrib.admin.forms import (
|
|
8
8
|
from django.contrib.admin.forms import (
|
9
9
|
AdminPasswordChangeForm as BaseAdminOwnPasswordChangeForm,
|
10
10
|
)
|
11
|
+
from django.contrib.admin.views.main import ChangeListSearchForm
|
11
12
|
from django.contrib.auth.forms import (
|
12
13
|
AdminPasswordChangeForm as BaseAdminPasswordChangeForm,
|
13
14
|
)
|
@@ -85,7 +86,7 @@ class ActionForm(forms.Form):
|
|
85
86
|
class AuthenticationForm(AdminAuthenticationForm):
|
86
87
|
def __init__(
|
87
88
|
self,
|
88
|
-
request:
|
89
|
+
request: HttpRequest | None = None,
|
89
90
|
*args,
|
90
91
|
**kwargs,
|
91
92
|
) -> None:
|
@@ -192,14 +193,14 @@ class Fieldline(BaseFieldline):
|
|
192
193
|
|
193
194
|
|
194
195
|
class PaginationFormSetMixin:
|
195
|
-
queryset:
|
196
|
-
request:
|
197
|
-
per_page:
|
196
|
+
queryset: QuerySet | None = None
|
197
|
+
request: HttpRequest | None = None
|
198
|
+
per_page: int | None = None
|
198
199
|
|
199
200
|
def __init__(
|
200
201
|
self,
|
201
|
-
request:
|
202
|
-
per_page:
|
202
|
+
request: HttpRequest | None = None,
|
203
|
+
per_page: int | None = None,
|
203
204
|
*args,
|
204
205
|
**kwargs,
|
205
206
|
):
|
@@ -240,3 +241,14 @@ class PaginationInlineFormSet(PaginationFormSetMixin, BaseInlineFormSet):
|
|
240
241
|
|
241
242
|
class PaginationGenericInlineFormSet(PaginationFormSetMixin, BaseGenericInlineFormSet):
|
242
243
|
pass
|
244
|
+
|
245
|
+
|
246
|
+
class DatasetChangeListSearchForm(ChangeListSearchForm):
|
247
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
248
|
+
super().__init__(*args, **kwargs)
|
249
|
+
|
250
|
+
from django.contrib.admin.views.main import SEARCH_VAR
|
251
|
+
|
252
|
+
self.fields = {
|
253
|
+
SEARCH_VAR: forms.CharField(required=False, strip=False),
|
254
|
+
}
|
@@ -1,4 +1,5 @@
|
|
1
|
-
from
|
1
|
+
from collections.abc import Callable
|
2
|
+
from typing import Any
|
2
3
|
|
3
4
|
from django.db.models import Model
|
4
5
|
from django.forms import Form
|
@@ -22,7 +23,7 @@ class ActionModelAdminMixin:
|
|
22
23
|
actions_submit_line = () # Displayed in changeform in the submit line (form buttons)
|
23
24
|
|
24
25
|
def changelist_view(
|
25
|
-
self, request: HttpRequest, extra_context:
|
26
|
+
self, request: HttpRequest, extra_context: dict[str, str] | None = None
|
26
27
|
) -> TemplateResponse:
|
27
28
|
"""
|
28
29
|
Changelist contains `actions_list` and `actions_row` custom actions. In case of `actions_row` they
|
@@ -60,9 +61,9 @@ class ActionModelAdminMixin:
|
|
60
61
|
def changeform_view(
|
61
62
|
self,
|
62
63
|
request: HttpRequest,
|
63
|
-
object_id:
|
64
|
+
object_id: str | None = None,
|
64
65
|
form_url: str = "",
|
65
|
-
extra_context:
|
66
|
+
extra_context: dict[str, Any] | None = None,
|
66
67
|
) -> Any:
|
67
68
|
"""
|
68
69
|
Changeform contains `actions_submit_line` and `actions_detail` custom actions.
|
@@ -159,7 +160,7 @@ class ActionModelAdminMixin:
|
|
159
160
|
request, self._get_base_actions_submit_line(), object_id
|
160
161
|
)
|
161
162
|
|
162
|
-
def _extract_action_names(self, actions: list[
|
163
|
+
def _extract_action_names(self, actions: list[str | dict]) -> list[str]:
|
163
164
|
"""
|
164
165
|
Gets the list of only actions names from the actions structure provided in ModelAdmin
|
165
166
|
"""
|
@@ -228,10 +229,10 @@ class ActionModelAdminMixin:
|
|
228
229
|
|
229
230
|
def _get_actions_navigation(
|
230
231
|
self,
|
231
|
-
provided_actions: list[
|
232
|
+
provided_actions: list[str | dict],
|
232
233
|
allowed_actions: list[UnfoldAction],
|
233
|
-
object_id:
|
234
|
-
) -> list[
|
234
|
+
object_id: str | None = None,
|
235
|
+
) -> list[str | dict]:
|
235
236
|
"""
|
236
237
|
Builds navigation structure for the actions which is going to be provided to the template.
|
237
238
|
"""
|
@@ -272,7 +273,7 @@ class ActionModelAdminMixin:
|
|
272
273
|
"path": get_action_path(action),
|
273
274
|
}
|
274
275
|
|
275
|
-
def build_dropdown(nav_item: dict) ->
|
276
|
+
def build_dropdown(nav_item: dict) -> dict | None:
|
276
277
|
"""
|
277
278
|
Builds a dropdown structure for the action.
|
278
279
|
"""
|
@@ -304,7 +305,7 @@ class ActionModelAdminMixin:
|
|
304
305
|
self,
|
305
306
|
request: HttpRequest,
|
306
307
|
actions: list[UnfoldAction],
|
307
|
-
object_id:
|
308
|
+
object_id: int | str | None = None,
|
308
309
|
) -> list[UnfoldAction]:
|
309
310
|
"""
|
310
311
|
Filters out actions that the user doesn't have access to.
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import copy
|
2
|
-
from typing import Any
|
2
|
+
from typing import Any
|
3
3
|
|
4
4
|
from django.contrib.admin import helpers
|
5
5
|
from django.contrib.admin.sites import AdminSite
|
@@ -31,9 +31,9 @@ class BaseModelAdminMixin:
|
|
31
31
|
def changeform_view(
|
32
32
|
self,
|
33
33
|
request: HttpRequest,
|
34
|
-
object_id:
|
34
|
+
object_id: str | None = None,
|
35
35
|
form_url: str = "",
|
36
|
-
extra_context:
|
36
|
+
extra_context: dict[str, Any] | None = None,
|
37
37
|
) -> Any:
|
38
38
|
from unfold.forms import AdminForm, Fieldline
|
39
39
|
|
@@ -61,7 +61,7 @@ class BaseModelAdminMixin:
|
|
61
61
|
|
62
62
|
def formfield_for_foreignkey(
|
63
63
|
self, db_field: ForeignKey, request: HttpRequest, **kwargs
|
64
|
-
) ->
|
64
|
+
) -> ModelChoiceField | None:
|
65
65
|
db = kwargs.get("using")
|
66
66
|
|
67
67
|
# Overrides widgets for all related fields
|
@@ -102,7 +102,7 @@ class BaseModelAdminMixin:
|
|
102
102
|
|
103
103
|
def formfield_for_nullboolean_field(
|
104
104
|
self, db_field: Field, request: HttpRequest, **kwargs
|
105
|
-
) ->
|
105
|
+
) -> Field | None:
|
106
106
|
if "widget" not in kwargs:
|
107
107
|
if db_field.choices:
|
108
108
|
kwargs["widget"] = widgets.UnfoldAdminSelectWidget(
|
@@ -115,7 +115,7 @@ class BaseModelAdminMixin:
|
|
115
115
|
|
116
116
|
def formfield_for_dbfield(
|
117
117
|
self, db_field: Field, request: HttpRequest, **kwargs
|
118
|
-
) ->
|
118
|
+
) -> Field | None:
|
119
119
|
if isinstance(db_field, models.BooleanField) and db_field.null is True:
|
120
120
|
return self.formfield_for_nullboolean_field(db_field, request, **kwargs)
|
121
121
|
|
unfold/sites.py
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
import copy
|
2
2
|
import time
|
3
|
+
from collections.abc import Callable
|
3
4
|
from http import HTTPStatus
|
4
|
-
from typing import Any
|
5
|
+
from typing import Any
|
5
6
|
from urllib.parse import parse_qs, urlparse
|
6
7
|
|
7
8
|
from django.contrib.admin import AdminSite
|
@@ -141,7 +142,7 @@ class UnfoldAdminSite(AdminSite):
|
|
141
142
|
return context
|
142
143
|
|
143
144
|
def index(
|
144
|
-
self, request: HttpRequest, extra_context:
|
145
|
+
self, request: HttpRequest, extra_context: dict[str, Any] | None = None
|
145
146
|
) -> TemplateResponse:
|
146
147
|
app_list = self.get_app_list(request)
|
147
148
|
|
@@ -166,7 +167,7 @@ class UnfoldAdminSite(AdminSite):
|
|
166
167
|
)
|
167
168
|
|
168
169
|
def toggle_sidebar(
|
169
|
-
self, request: HttpRequest, extra_context:
|
170
|
+
self, request: HttpRequest, extra_context: dict[str, Any] | None = None
|
170
171
|
) -> HttpResponse:
|
171
172
|
if "toggle_sidebar" not in request.session:
|
172
173
|
request.session["toggle_sidebar"] = True
|
@@ -214,14 +215,14 @@ class UnfoldAdminSite(AdminSite):
|
|
214
215
|
request: HttpRequest,
|
215
216
|
app_list: list[dict[str, Any]],
|
216
217
|
search_term: str,
|
217
|
-
allowed_models:
|
218
|
+
allowed_models: list[str] | None = None,
|
218
219
|
) -> list[SearchResult]:
|
219
220
|
results = []
|
220
221
|
|
221
222
|
for app in app_list:
|
222
223
|
for model in app["models"]:
|
223
224
|
# Skip models which are not allowed
|
224
|
-
if isinstance(allowed_models,
|
225
|
+
if isinstance(allowed_models, list | tuple):
|
225
226
|
if model["model"]._meta.label.lower() not in [
|
226
227
|
m.lower() for m in allowed_models
|
227
228
|
]:
|
@@ -263,7 +264,7 @@ class UnfoldAdminSite(AdminSite):
|
|
263
264
|
return results
|
264
265
|
|
265
266
|
def search(
|
266
|
-
self, request: HttpRequest, extra_context:
|
267
|
+
self, request: HttpRequest, extra_context: dict[str, Any] | None = None
|
267
268
|
) -> TemplateResponse:
|
268
269
|
start_time = time.time()
|
269
270
|
|
@@ -302,10 +303,10 @@ class UnfoldAdminSite(AdminSite):
|
|
302
303
|
self._get_config("COMMAND", request).get("search_models"), request
|
303
304
|
)
|
304
305
|
|
305
|
-
if search_models is True or isinstance(search_models,
|
306
|
+
if search_models is True or isinstance(search_models, list | tuple):
|
306
307
|
allowed_models = (
|
307
308
|
search_models
|
308
|
-
if isinstance(search_models,
|
309
|
+
if isinstance(search_models, list | tuple)
|
309
310
|
else None
|
310
311
|
)
|
311
312
|
|
@@ -340,7 +341,7 @@ class UnfoldAdminSite(AdminSite):
|
|
340
341
|
)
|
341
342
|
|
342
343
|
def password_change(
|
343
|
-
self, request: HttpRequest, extra_context:
|
344
|
+
self, request: HttpRequest, extra_context: dict[str, Any] | None = None
|
344
345
|
) -> HttpResponse:
|
345
346
|
from django.contrib.auth.views import PasswordChangeView
|
346
347
|
|
@@ -462,7 +463,7 @@ class UnfoldAdminSite(AdminSite):
|
|
462
463
|
return tabs
|
463
464
|
|
464
465
|
def _call_permission_callback(
|
465
|
-
self, callback:
|
466
|
+
self, callback: str | Callable | None, request: HttpRequest
|
466
467
|
) -> bool:
|
467
468
|
if callback is None:
|
468
469
|
return True
|
@@ -490,7 +491,7 @@ class UnfoldAdminSite(AdminSite):
|
|
490
491
|
return target
|
491
492
|
|
492
493
|
def _get_is_active(
|
493
|
-
self, request: HttpRequest, link:
|
494
|
+
self, request: HttpRequest, link: str | Callable, is_tab: bool = False
|
494
495
|
) -> bool:
|
495
496
|
if not isinstance(link, str):
|
496
497
|
link = str(link)
|
@@ -545,9 +546,7 @@ class UnfoldAdminSite(AdminSite):
|
|
545
546
|
if key in config and config[key]:
|
546
547
|
return self._get_value(config[key], *args)
|
547
548
|
|
548
|
-
def _get_theme_images(
|
549
|
-
self, key: str, *args: Any
|
550
|
-
) -> Union[dict[str, str], str, None]:
|
549
|
+
def _get_theme_images(self, key: str, *args: Any) -> dict[str, str] | str | None:
|
551
550
|
images = self._get_config(key, *args)
|
552
551
|
|
553
552
|
if isinstance(images, dict):
|
@@ -613,9 +612,7 @@ class UnfoldAdminSite(AdminSite):
|
|
613
612
|
for item in items
|
614
613
|
]
|
615
614
|
|
616
|
-
def _get_value(
|
617
|
-
self, value: Union[str, Callable, lazy, None], *args: Any
|
618
|
-
) -> Optional[str]:
|
615
|
+
def _get_value(self, value: str | Callable | None, *args: Any) -> str | None:
|
619
616
|
if value is None:
|
620
617
|
return None
|
621
618
|
|