django-unfold 0.29.1__py3-none-any.whl → 0.31.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.29.1.dist-info → django_unfold-0.31.0.dist-info}/METADATA +63 -19
- {django_unfold-0.29.1.dist-info → django_unfold-0.31.0.dist-info}/RECORD +74 -73
- unfold/admin.py +32 -11
- unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html +3 -3
- unfold/contrib/forms/static/unfold/forms/js/trix.js +2 -2
- unfold/contrib/forms/templates/unfold/forms/array.html +3 -1
- unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html +6 -6
- unfold/contrib/forms/templates/unfold/forms/wysiwyg.html +1 -1
- unfold/contrib/forms/widgets.py +22 -11
- unfold/contrib/guardian/templates/unfold/guardian/group_form.html +4 -4
- unfold/contrib/guardian/templates/unfold/guardian/user_form.html +4 -4
- unfold/contrib/import_export/templates/admin/import_export/change_form.html +1 -1
- unfold/contrib/import_export/templates/admin/import_export/import_errors.html +1 -1
- unfold/contrib/import_export/templates/admin/import_export/import_preview.html +3 -3
- unfold/contrib/import_export/templates/admin/import_export/import_validation.html +4 -4
- unfold/contrib/inlines/forms.py +1 -2
- unfold/contrib/simple_history/templates/simple_history/object_history_list.html +9 -9
- unfold/contrib/simple_history/templates/simple_history/submit_line.html +1 -1
- unfold/dataclasses.py +10 -1
- unfold/fields.py +2 -2
- unfold/forms.py +18 -3
- unfold/settings.py +1 -0
- unfold/sites.py +39 -15
- unfold/static/unfold/css/styles.css +1 -1
- unfold/static/unfold/js/alpine.anchor.js +1 -0
- unfold/static/unfold/js/alpine.js +2 -2
- unfold/static/unfold/js/alpine.persist.js +1 -1
- unfold/static/unfold/js/app.js +45 -3
- unfold/styles.css +15 -11
- unfold/templates/admin/actions.html +1 -1
- unfold/templates/admin/app_list.html +1 -1
- unfold/templates/admin/base.html +4 -4
- unfold/templates/admin/change_list.html +2 -2
- unfold/templates/admin/change_list_results.html +3 -3
- unfold/templates/admin/delete_confirmation.html +4 -4
- unfold/templates/admin/delete_selected_confirmation.html +4 -4
- unfold/templates/admin/edit_inline/stacked.html +2 -2
- unfold/templates/admin/edit_inline/tabular.html +4 -4
- unfold/templates/admin/filter.html +2 -2
- unfold/templates/admin/includes/fieldset.html +1 -1
- unfold/templates/admin/includes/object_delete_summary.html +1 -1
- unfold/templates/admin/login.html +8 -8
- unfold/templates/admin/object_history.html +4 -4
- unfold/templates/admin/search_form.html +1 -1
- unfold/templates/admin/submit_line.html +7 -5
- unfold/templates/auth/widgets/read_only_password_hash.html +1 -1
- unfold/templates/registration/logged_out.html +1 -1
- unfold/templates/unfold/change_list_filter.html +10 -2
- unfold/templates/unfold/helpers/account_links.html +2 -2
- unfold/templates/unfold/helpers/actions_row.html +4 -4
- unfold/templates/unfold/helpers/app_list.html +48 -38
- unfold/templates/unfold/helpers/app_list_default.html +4 -4
- unfold/templates/unfold/helpers/breadcrumb_item.html +1 -1
- unfold/templates/unfold/helpers/field_readonly_value.html +1 -1
- unfold/templates/unfold/helpers/fieldset_row.html +7 -7
- unfold/templates/unfold/helpers/fieldsets_tabs.html +2 -2
- unfold/templates/unfold/helpers/header.html +1 -1
- unfold/templates/unfold/helpers/help_text.html +1 -1
- unfold/templates/unfold/helpers/history.html +1 -1
- unfold/templates/unfold/helpers/label.html +1 -1
- unfold/templates/unfold/helpers/search.html +7 -4
- unfold/templates/unfold/helpers/search_results.html +2 -2
- unfold/templates/unfold/helpers/tab_action.html +1 -1
- unfold/templates/unfold/helpers/tab_list.html +27 -5
- unfold/templates/unfold/helpers/theme_switch.html +2 -2
- unfold/templates/unfold/layouts/skeleton.html +6 -1
- unfold/templates/unfold/widgets/clearable_file_input.html +14 -6
- unfold/templates/unfold/widgets/clearable_file_input_small.html +4 -4
- unfold/templates/unfold/widgets/split_datetime.html +2 -2
- unfold/templatetags/unfold.py +33 -12
- unfold/templatetags/unfold_list.py +16 -6
- unfold/widgets.py +11 -4
- {django_unfold-0.29.1.dist-info → django_unfold-0.31.0.dist-info}/LICENSE.md +0 -0
- {django_unfold-0.29.1.dist-info → django_unfold-0.31.0.dist-info}/WHEEL +0 -0
@@ -15,7 +15,9 @@
|
|
15
15
|
|
16
16
|
<template x-for="(item, index) in items" :key="item.key">
|
17
17
|
<div class="flex flex-row">
|
18
|
-
{%
|
18
|
+
{% with widget=template %}
|
19
|
+
{% include template.template_name %}
|
20
|
+
{% endwith %}
|
19
21
|
|
20
22
|
<a x-on:click="items.splice(index, 1)" class="bg-white border cursor-pointer flex items-center h-9.5 justify-center ml-2 rounded shadow-sm shrink-0 text-red-600 text-sm w-9.5 dark:bg-gray-900 dark:border-gray-700 dark:text-red-500">
|
21
23
|
<span class="material-symbols-outlined text-sm">delete</span>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
<template id="trix-toolbar">
|
4
4
|
<div class="bg-gray-50 flex flex-row flex-wrap border group-[.errors]:border-t-red-600 gap-4 group-[.errors]:border-x-red-600 px-3 py-2 relative rounded-t-md shadow-sm w-full dark:bg-white/[.02] dark:border-gray-700 dark:group-[.errors]:border-t-red-500 dark:group-[.errors]:border-x-red-500">
|
5
|
-
<div data-trix-button-group="text-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm shrink-0 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
5
|
+
<div data-trix-button-group="text-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm shrink-0 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
6
6
|
{% spaceless %}
|
7
7
|
<button type="button" data-trix-attribute="p" data-trix-key="p" title="{% trans "Paragraph" %}" tabindex="-1" class="border-r cursor-pointer flex items-center h-8 justify-center transition-colors w-8 hover:text-primary-600 dark:border-gray-700">
|
8
8
|
<span class="material-symbols-outlined">format_paragraph</span>
|
@@ -30,7 +30,7 @@
|
|
30
30
|
{% endspaceless %}
|
31
31
|
</div>
|
32
32
|
|
33
|
-
<div data-trix-button-group="block-headings" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
33
|
+
<div data-trix-button-group="block-headings" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
34
34
|
{% spaceless %}
|
35
35
|
<button type="button" data-trix-attribute="heading1" title="{% trans "Heading" %} 1" tabindex="-1" class="border-r cursor-pointer flex items-center h-8 justify-center transition-colors w-8 hover:text-primary-600 dark:border-gray-700">
|
36
36
|
<span class="material-symbols-outlined">format_h1</span>
|
@@ -50,7 +50,7 @@
|
|
50
50
|
{% endspaceless %}
|
51
51
|
</div>
|
52
52
|
|
53
|
-
<div data-trix-button-group="block-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
53
|
+
<div data-trix-button-group="block-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
54
54
|
{% spaceless %}
|
55
55
|
<button type="button" data-trix-attribute="quote" title="{% trans "Quote" %}" tabindex="-1" class="border-r cursor-pointer flex items-center h-8 justify-center transition-colors w-8 hover:text-primary-600 dark:border-gray-700">
|
56
56
|
<span class="material-symbols-outlined">format_quote</span>
|
@@ -79,7 +79,7 @@
|
|
79
79
|
</div>
|
80
80
|
|
81
81
|
<div class="lg:ml-auto">
|
82
|
-
<div data-trix-button-group="history-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
82
|
+
<div data-trix-button-group="history-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
83
83
|
<button type="button" data-trix-action="undo" data-trix-key="z" title="{% trans "Undo" %}" tabindex="-1" class="border-r cursor-pointer flex items-center h-8 justify-center transition-colors w-8 hover:text-primary-600 dark:border-gray-700">
|
84
84
|
<span class="material-symbols-outlined">undo</span>
|
85
85
|
</button>
|
@@ -92,9 +92,9 @@
|
|
92
92
|
<div data-trix-dialogs>
|
93
93
|
<div class="absolute bg-white border-b -bottom-px left-0 px-4 py-2 right-0 shadow-sm translate-y-full dark:bg-gray-900 dark:border-gray-700" data-trix-dialog="href" data-trix-dialog-attribute="href">
|
94
94
|
<div class="flex flex-row">
|
95
|
-
<input type="url" name="href" class="border bg-white font-medium px-3 rounded-md shadow-sm text-gray-500 text-sm focus:ring focus:ring-primary-300 focus:border-primary-600 focus:outline-none group-[.errors]:border-red-600 group-[.errors]:focus:ring-red-200 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
95
|
+
<input type="url" name="href" class="border bg-white font-medium px-3 rounded-md shadow-sm text-gray-500 text-sm focus:ring focus:ring-primary-300 focus:border-primary-600 focus:outline-none group-[.errors]:border-red-600 group-[.errors]:focus:ring-red-200 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300 dark:focus:ring-primary-600/30 dark:group-[.errors]:border-red-500 dark:group-[.errors]:focus:ring-red-600/40" placeholder="{% trans "Enter an URL" %}" required data-trix-input>
|
96
96
|
|
97
|
-
<div class="bg-white border border-md flex flex-row ml-4 rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
97
|
+
<div class="bg-white border border-md flex flex-row ml-4 rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
98
98
|
<button type="button" data-trix-method="setAttribute" title="{% trans "Link" %}" class="border-r cursor-pointer flex items-center h-8 justify-center text-gray-400 transition-colors w-8 hover:text-primary-600 dark:border-gray-700">
|
99
99
|
<span class="material-symbols-outlined">link</span>
|
100
100
|
</button>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
{% include "unfold/forms/helpers/toolbar.html" %}
|
2
2
|
|
3
|
-
<input type="hidden" name="{{ widget.name }}" id="wysiwyg-{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value }}"{% endif %}>
|
3
|
+
<input type="hidden" name="{{ widget.name }}" id="wysiwyg-{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value }}"{% endif %} {% include "django/forms/widgets/attrs.html" %}>
|
4
4
|
|
5
5
|
<div class="max-w-4xl relative">
|
6
6
|
<trix-editor input="wysiwyg-{{ widget.name }}" {% include "django/forms/widgets/attrs.html" %}></trix-editor>
|
unfold/contrib/forms/widgets.py
CHANGED
@@ -4,7 +4,11 @@ from django.core.validators import EMPTY_VALUES
|
|
4
4
|
from django.forms import MultiWidget, Widget
|
5
5
|
from django.http import QueryDict
|
6
6
|
from django.utils.datastructures import MultiValueDict
|
7
|
-
from unfold.widgets import
|
7
|
+
from unfold.widgets import (
|
8
|
+
PROSE_CLASSES,
|
9
|
+
UnfoldAdminSelectWidget,
|
10
|
+
UnfoldAdminTextInputWidget,
|
11
|
+
)
|
8
12
|
|
9
13
|
WYSIWYG_CLASSES = [
|
10
14
|
*PROSE_CLASSES,
|
@@ -20,26 +24,33 @@ WYSIWYG_CLASSES = [
|
|
20
24
|
"w-full",
|
21
25
|
"focus:outline-none",
|
22
26
|
"dark:border-gray-700",
|
23
|
-
"dark:text-gray-
|
27
|
+
"dark:text-gray-300",
|
24
28
|
"dark:group-[.errors]:border-red-500",
|
25
29
|
]
|
26
30
|
|
27
31
|
|
28
32
|
class ArrayWidget(MultiWidget):
|
29
33
|
template_name = "unfold/forms/array.html"
|
30
|
-
widget_class = UnfoldAdminTextInputWidget
|
31
34
|
|
32
35
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
33
|
-
|
36
|
+
if "choices" in kwargs:
|
37
|
+
self.choices = kwargs["choices"]
|
38
|
+
|
39
|
+
widgets = [self.get_widget_instance()]
|
34
40
|
super().__init__(widgets)
|
35
41
|
|
42
|
+
def get_widget_instance(self) -> Any:
|
43
|
+
if hasattr(self, "choices"):
|
44
|
+
return UnfoldAdminSelectWidget(choices=self.choices)
|
45
|
+
|
46
|
+
return UnfoldAdminTextInputWidget()
|
47
|
+
|
36
48
|
def get_context(self, name: str, value: str, attrs: Dict) -> Dict:
|
37
49
|
self._resolve_widgets(value)
|
38
50
|
context = super().get_context(name, value, attrs)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
context.update({"template": template_widget})
|
51
|
+
context.update(
|
52
|
+
{"template": self.get_widget_instance().get_context(name, "", {})["widget"]}
|
53
|
+
)
|
43
54
|
return context
|
44
55
|
|
45
56
|
def value_from_datadict(
|
@@ -71,12 +82,12 @@ class ArrayWidget(MultiWidget):
|
|
71
82
|
value = []
|
72
83
|
|
73
84
|
elif isinstance(value, List):
|
74
|
-
self.widgets = [self.
|
85
|
+
self.widgets = [self.get_widget_instance() for item in value]
|
75
86
|
else:
|
76
|
-
self.widgets = [self.
|
87
|
+
self.widgets = [self.get_widget_instance() for item in value.split(",")]
|
77
88
|
|
78
89
|
self.widgets_names = ["" for i in range(len(self.widgets))]
|
79
|
-
self.widgets = [w
|
90
|
+
self.widgets = [w if isinstance(w, type) else w for w in self.widgets]
|
80
91
|
|
81
92
|
|
82
93
|
class WysiwygWidget(Widget):
|
@@ -8,7 +8,7 @@
|
|
8
8
|
</h2>
|
9
9
|
|
10
10
|
{% if groups_perms.items %}
|
11
|
-
<table id="group-permissions" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-
|
11
|
+
<table id="group-permissions" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-300 lg:border lg:rounded-md lg:shadow-sm lg:dark:border-gray-800">
|
12
12
|
<thead class="hidden lg:table-header-group">
|
13
13
|
<tr>
|
14
14
|
<th class="align-middle font-medium px-3 py-2 text-left text-gray-400 text-sm">
|
@@ -30,14 +30,14 @@
|
|
30
30
|
<tbody>
|
31
31
|
{% for group, group_perms in groups_perms.items %}
|
32
32
|
<tr class="block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
33
|
-
<th class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
33
|
+
<th class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans "User" %}">
|
34
34
|
<span class="text-gray-700 dark:text-gray-200">
|
35
35
|
{{ group }}
|
36
36
|
</span>
|
37
37
|
</th>
|
38
38
|
|
39
39
|
{% for perm in model_perms %}
|
40
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
40
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{{ perm.name }}">
|
41
41
|
{% if perm.codename in group_perms %}
|
42
42
|
{% include "unfold/helpers/boolean.html" with value=False %}
|
43
43
|
{% else %}
|
@@ -46,7 +46,7 @@
|
|
46
46
|
</td>
|
47
47
|
{% endfor %}
|
48
48
|
|
49
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-right text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
49
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-right text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans "Action" %}">
|
50
50
|
<a href="group-manage/{{ group.id|safe }}/" class="hover:text-gray-700 dark:hover:text-white">
|
51
51
|
{% trans "Edit" %}
|
52
52
|
</a>
|
@@ -8,7 +8,7 @@
|
|
8
8
|
</h2>
|
9
9
|
|
10
10
|
{% if users_perms.items %}
|
11
|
-
<table id="user-permissions" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-
|
11
|
+
<table id="user-permissions" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-300 lg:border lg:rounded-md lg:shadow-sm lg:dark:border-gray-800">
|
12
12
|
<thead class="hidden lg:table-header-group">
|
13
13
|
<tr>
|
14
14
|
<th class="align-middle font-medium px-3 py-2 text-left text-gray-400 text-sm">
|
@@ -30,14 +30,14 @@
|
|
30
30
|
<tbody>
|
31
31
|
{% for user, user_perms in users_perms.items %}
|
32
32
|
<tr class="block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
33
|
-
<th class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
33
|
+
<th class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans "User" %}">
|
34
34
|
<span class="text-gray-700 dark:text-gray-200">
|
35
35
|
{{ user }}
|
36
36
|
</span>
|
37
37
|
</th>
|
38
38
|
|
39
39
|
{% for perm in model_perms %}
|
40
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
40
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{{ perm.name }}">
|
41
41
|
{% if perm.codename in group_perms %}
|
42
42
|
{% include "unfold/helpers/boolean.html" with value=False %}
|
43
43
|
{% else %}
|
@@ -46,7 +46,7 @@
|
|
46
46
|
</td>
|
47
47
|
{% endfor %}
|
48
48
|
|
49
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-right text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
49
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-right text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans "Action" %}">
|
50
50
|
<a href="user-manage/{{ user.id|safe }}/" class="hover:text-gray-700 dark:hover:text-white">
|
51
51
|
{% trans "Edit" %}
|
52
52
|
</a>
|
@@ -5,6 +5,6 @@
|
|
5
5
|
{{ block.super }}
|
6
6
|
|
7
7
|
{% if show_change_form_export %}
|
8
|
-
<input type="submit" value="{% translate 'Export' %}" name="_export-item" class="bg-white text-gray-500 border cursor-pointer flex font-medium items-center px-3 py-2 mr-3 rounded-md shadow-sm text-sm dark:bg-gray-900 dark:border dark:border-gray-700 dark:text-gray-
|
8
|
+
<input type="submit" value="{% translate 'Export' %}" name="_export-item" class="bg-white text-gray-500 border cursor-pointer flex font-medium items-center px-3 py-2 mr-3 rounded-md shadow-sm text-sm dark:bg-gray-900 dark:border dark:border-gray-700 dark:text-gray-300">
|
9
9
|
{% endif %}
|
10
10
|
{% endblock %}
|
@@ -26,7 +26,7 @@
|
|
26
26
|
</div>
|
27
27
|
</div>
|
28
28
|
|
29
|
-
<div class="block border leading-relaxed rounded p-4 text-sm traceback dark:border-gray-800 dark:text-gray-
|
29
|
+
<div class="block border leading-relaxed rounded p-4 text-sm traceback dark:border-gray-800 dark:text-gray-300 " x-show="open">
|
30
30
|
{{ error.traceback|linebreaks }}
|
31
31
|
</div>
|
32
32
|
</li>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{% load admin_urls i18n import_export_tags unfold %}
|
2
2
|
|
3
3
|
{% block preview %}
|
4
|
-
<table class="border-gray-200 border-spacing-none border-separate text-gray-700 w-full dark:text-gray-
|
4
|
+
<table class="border-gray-200 border-spacing-none border-separate text-gray-700 w-full dark:text-gray-300 lg:border lg:rounded-md lg:shadow-sm lg:dark:border-gray-800">
|
5
5
|
<thead class="hidden lg:table-header-group">
|
6
6
|
<tr>
|
7
7
|
<th class="align-middle capitalize font-medium px-3 py-2 text-left text-gray-400 text-sm"></th>
|
@@ -17,7 +17,7 @@
|
|
17
17
|
<tbody>
|
18
18
|
{% for row in result.valid_rows %}
|
19
19
|
<tr class="{{ row.import_type }} {% cycle '' 'bg-gray-50 dark:bg-white/[.02]' %} block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
20
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-
|
20
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-300 lg:before:hidden lg:py-3 lg:table-cell dark:border-gray-800">
|
21
21
|
{% if row.import_type == 'new' %}
|
22
22
|
{% trans "New" %}
|
23
23
|
{% elif row.import_type == 'skip' %}
|
@@ -30,7 +30,7 @@
|
|
30
30
|
</td>
|
31
31
|
|
32
32
|
{% for field in row.diff %}
|
33
|
-
<td data-label="{{ result.diff_headers|index:forloop.counter0 }}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-
|
33
|
+
<td data-label="{{ result.diff_headers|index:forloop.counter0 }}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-300 lg:before:hidden lg:py-3 lg:table-cell dark:border-gray-800">
|
34
34
|
{{ field }}
|
35
35
|
</td>
|
36
36
|
{% endfor %}
|
@@ -15,7 +15,7 @@
|
|
15
15
|
</div>
|
16
16
|
</div>
|
17
17
|
|
18
|
-
<table class="border-gray-200 border-spacing-none border-separate text-gray-700 w-full dark:text-gray-
|
18
|
+
<table class="border-gray-200 border-spacing-none border-separate text-gray-700 w-full dark:text-gray-300 lg:border lg:rounded-md lg:shadow-sm lg:dark:border-gray-800">
|
19
19
|
<thead class="hidden lg:table-header-group">
|
20
20
|
<tr>
|
21
21
|
<th class="align-middle capitalize font-medium px-3 py-2 text-left text-gray-400 text-sm">
|
@@ -37,11 +37,11 @@
|
|
37
37
|
<tbody>
|
38
38
|
{% for row in result.invalid_rows %}
|
39
39
|
<tr class="{% cycle '' 'bg-gray-50 dark:bg-white/[.02]' %} block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
40
|
-
<td data-label="{% trans "Row" %}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-
|
40
|
+
<td data-label="{% trans "Row" %}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-300 lg:before:hidden lg:py-3 lg:table-cell dark:border-gray-800">
|
41
41
|
{{ row.number }}
|
42
42
|
</td>
|
43
43
|
|
44
|
-
<td data-label="{% trans "Errors" %}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-
|
44
|
+
<td data-label="{% trans "Errors" %}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-300 lg:before:hidden lg:py-3 lg:table-cell dark:border-gray-800">
|
45
45
|
<div>
|
46
46
|
<span class="bg-red-600 font-semibold ml-2 px-1 rounded-sm text-xs text-white">{{ row.error_count }}</span>
|
47
47
|
</div>
|
@@ -77,7 +77,7 @@
|
|
77
77
|
</td>
|
78
78
|
|
79
79
|
{% for field in row.values %}
|
80
|
-
<td data-label="{{ result.diff_headers|index:forloop.counter0 }}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-
|
80
|
+
<td data-label="{{ result.diff_headers|index:forloop.counter0 }}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-300 lg:before:hidden lg:py-3 lg:table-cell dark:border-gray-800">
|
81
81
|
{{ field }}
|
82
82
|
</td>
|
83
83
|
{% endfor %}
|
unfold/contrib/inlines/forms.py
CHANGED
@@ -12,9 +12,8 @@ class NonrelatedInlineModelFormSet(BaseModelFormSet):
|
|
12
12
|
**kwargs: Any,
|
13
13
|
) -> None:
|
14
14
|
self.instance = instance
|
15
|
-
self.queryset = self.provided_queryset
|
16
|
-
|
17
15
|
super().__init__(**kwargs)
|
16
|
+
self.queryset = self.provided_queryset
|
18
17
|
|
19
18
|
@classmethod
|
20
19
|
def get_default_prefix(cls: BaseModelFormSet) -> str:
|
@@ -3,7 +3,7 @@
|
|
3
3
|
{% load admin_urls %}
|
4
4
|
{% load getattribute from getattributes %}
|
5
5
|
|
6
|
-
<table id="change-history" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-
|
6
|
+
<table id="change-history" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-300 lg:border lg:rounded-md lg:shadow-sm lg:dark:border-gray-800">
|
7
7
|
<thead class="hidden lg:table-header-group">
|
8
8
|
<tr>
|
9
9
|
<th class="align-middle font-medium px-3 py-2 text-left text-gray-400 text-sm">
|
@@ -41,27 +41,27 @@
|
|
41
41
|
<tbody>
|
42
42
|
{% for record in historical_records %}
|
43
43
|
<tr class="block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
44
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
44
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Object' %}">
|
45
45
|
<a href="{% url opts|admin_urlname:'simple_history' object.pk record.pk %}">
|
46
46
|
{{ record.history_object }}
|
47
47
|
</a>
|
48
48
|
</td>
|
49
49
|
|
50
50
|
{% for column in history_list_display %}
|
51
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
51
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans column %}">
|
52
52
|
{{ record|getattribute:column }}
|
53
53
|
</th>
|
54
54
|
{% endfor %}
|
55
55
|
|
56
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
56
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Date/time' %}">
|
57
57
|
{{ record.history_date }}
|
58
58
|
</td>
|
59
59
|
|
60
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
60
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Comment' %}">
|
61
61
|
{{ record.get_history_type_display }}
|
62
62
|
</td>
|
63
63
|
|
64
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
64
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Changed by' %}">
|
65
65
|
{% if record.history_user %}
|
66
66
|
{% url admin_user_view record.history_user_id as admin_user_url %}
|
67
67
|
|
@@ -75,11 +75,11 @@
|
|
75
75
|
{% endif %}
|
76
76
|
</td>
|
77
77
|
|
78
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
78
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Change reason' %}">
|
79
79
|
{{ record.history_change_reason }}
|
80
80
|
</td>
|
81
81
|
|
82
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
82
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Changes' %}">
|
83
83
|
{% block history_delta_changes %}
|
84
84
|
{% if record.history_delta_changes %}
|
85
85
|
<ul>
|
@@ -97,7 +97,7 @@
|
|
97
97
|
{% endif %}
|
98
98
|
|
99
99
|
{% if change.old and change.new %}
|
100
|
-
<span class="align-text-top material-symbols-outlined md-18 text-gray-300 group-hover:text-gray-
|
100
|
+
<span class="align-text-top material-symbols-outlined md-18 text-gray-300 group-hover:text-gray-300 dark:text-gray-600">arrow_right_alt</span>
|
101
101
|
{% endif %}
|
102
102
|
|
103
103
|
{% if change.new %}
|
@@ -16,7 +16,7 @@
|
|
16
16
|
</button>
|
17
17
|
{% endif %}
|
18
18
|
|
19
|
-
<a href="{{ history_url }}" class="border font-medium mr-auto px-3 py-2 rounded-md text-sm text-gray-500 text-center transition-all w-full hover:bg-gray-50 lg:block lg:w-auto dark:border-gray-700 dark:text-gray-
|
19
|
+
<a href="{{ history_url }}" class="border font-medium mr-auto px-3 py-2 rounded-md text-sm text-gray-500 text-center transition-all w-full hover:bg-gray-50 lg:block lg:w-auto dark:border-gray-700 dark:text-gray-300 dark:hover:text-gray-200 dark:hover:bg-gray-900">
|
20
20
|
{% trans 'Close' %}
|
21
21
|
</a>
|
22
22
|
</div>
|
unfold/dataclasses.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
|
-
from typing import Dict, Optional
|
2
|
+
from typing import Callable, Dict, Optional, Union
|
3
3
|
|
4
4
|
from .typing import ActionFunction
|
5
5
|
|
@@ -11,3 +11,12 @@ class UnfoldAction:
|
|
11
11
|
description: str
|
12
12
|
path: str
|
13
13
|
attrs: Optional[Dict] = None
|
14
|
+
object_id: Optional[Union[int, str]] = None
|
15
|
+
|
16
|
+
|
17
|
+
@dataclass
|
18
|
+
class Favicon:
|
19
|
+
href: Union[str, Callable]
|
20
|
+
rel: Optional[str] = None
|
21
|
+
type: Optional[str] = None
|
22
|
+
sizes: Optional[str] = None
|
unfold/fields.py
CHANGED
@@ -95,7 +95,7 @@ class UnfoldAdminReadonlyField(helpers.AdminReadonlyField):
|
|
95
95
|
current_app=self.model_admin.admin_site.name,
|
96
96
|
)
|
97
97
|
return format_html(
|
98
|
-
'<a href="{}" class="text-primary-600 underline
|
98
|
+
'<a href="{}" class="text-primary-600 underline dark:text-primary-500">{}</a>',
|
99
99
|
url,
|
100
100
|
remote_obj,
|
101
101
|
)
|
@@ -148,7 +148,7 @@ class UnfoldAdminReadonlyField(helpers.AdminReadonlyField):
|
|
148
148
|
return conditional_escape(result_repr)
|
149
149
|
elif isinstance(f, models.URLField):
|
150
150
|
return format_html(
|
151
|
-
'<a href="{}" class="text-primary-600 underline whitespace-nowrap">{}</a>',
|
151
|
+
'<a href="{}" class="text-primary-600 underline whitespace-nowrap dark:text-primary-500">{}</a>',
|
152
152
|
value,
|
153
153
|
value,
|
154
154
|
)
|
unfold/forms.py
CHANGED
@@ -17,7 +17,13 @@ from django.http import HttpRequest
|
|
17
17
|
from django.utils.safestring import mark_safe
|
18
18
|
from django.utils.translation import gettext_lazy as _
|
19
19
|
|
20
|
-
from .widgets import
|
20
|
+
from .widgets import (
|
21
|
+
BASE_INPUT_CLASSES,
|
22
|
+
INPUT_CLASSES,
|
23
|
+
SELECT_CLASSES,
|
24
|
+
UnfoldAdminPasswordInput,
|
25
|
+
UnfoldAdminRadioSelectWidget,
|
26
|
+
)
|
21
27
|
|
22
28
|
|
23
29
|
class UnfoldReadOnlyPasswordHashWidget(ReadOnlyPasswordHashWidget):
|
@@ -65,8 +71,17 @@ class UserCreationForm(BaseUserCreationForm):
|
|
65
71
|
) -> None:
|
66
72
|
super().__init__(request, *args, **kwargs)
|
67
73
|
|
68
|
-
self.fields["password1"].widget
|
69
|
-
|
74
|
+
self.fields["password1"].widget = UnfoldAdminPasswordInput(
|
75
|
+
attrs={"autocomplete": "new-password"}
|
76
|
+
)
|
77
|
+
self.fields["password2"].widget = UnfoldAdminPasswordInput(
|
78
|
+
attrs={"autocomplete": "new-password"}
|
79
|
+
)
|
80
|
+
|
81
|
+
if "usable_password" in self.fields:
|
82
|
+
self.fields["usable_password"].widget = UnfoldAdminRadioSelectWidget(
|
83
|
+
choices=self.fields["usable_password"].choices,
|
84
|
+
)
|
70
85
|
|
71
86
|
|
72
87
|
class UserChangeForm(BaseUserChangeForm):
|
unfold/settings.py
CHANGED
unfold/sites.py
CHANGED
@@ -10,6 +10,7 @@ from django.urls import URLPattern, path, reverse, reverse_lazy
|
|
10
10
|
from django.utils.functional import lazy
|
11
11
|
from django.utils.module_loading import import_string
|
12
12
|
|
13
|
+
from .dataclasses import Favicon
|
13
14
|
from .settings import get_config
|
14
15
|
from .utils import hex_to_rgb
|
15
16
|
from .widgets import CHECKBOX_CLASSES, INPUT_CLASSES
|
@@ -66,6 +67,9 @@ class UnfoldAdminSite(AdminSite):
|
|
66
67
|
"site_symbol": self._get_value(
|
67
68
|
get_config(self.settings_name)["SITE_SYMBOL"], request
|
68
69
|
),
|
70
|
+
"site_favicons": self._process_favicons(
|
71
|
+
request, get_config(self.settings_name)["SITE_FAVICONS"]
|
72
|
+
),
|
69
73
|
"show_history": get_config(self.settings_name)["SHOW_HISTORY"],
|
70
74
|
"show_view_on_site": get_config(self.settings_name)[
|
71
75
|
"SHOW_VIEW_ON_SITE"
|
@@ -220,23 +224,14 @@ class UnfoldAdminSite(AdminSite):
|
|
220
224
|
navigation = get_config(self.settings_name)["SIDEBAR"].get("navigation", [])
|
221
225
|
results = []
|
222
226
|
|
223
|
-
def _get_is_active(link: str) -> bool:
|
224
|
-
if not isinstance(link, str):
|
225
|
-
link = str(link)
|
226
|
-
|
227
|
-
if link in request.path and link != reverse_lazy(f"{self.name}:index"):
|
228
|
-
return True
|
229
|
-
elif link == request.path == reverse_lazy(f"{self.name}:index"):
|
230
|
-
return True
|
231
|
-
|
232
|
-
return False
|
233
|
-
|
234
227
|
for group in navigation:
|
235
228
|
allowed_items = []
|
236
229
|
|
237
230
|
for item in group["items"]:
|
238
231
|
item["active"] = False
|
239
|
-
item["active"] = _get_is_active(
|
232
|
+
item["active"] = self._get_is_active(
|
233
|
+
request, item.get("link_callback") or item["link"]
|
234
|
+
)
|
240
235
|
|
241
236
|
for tab in get_config(self.settings_name)["TABS"]:
|
242
237
|
has_primary_link = False
|
@@ -247,7 +242,9 @@ class UnfoldAdminSite(AdminSite):
|
|
247
242
|
has_primary_link = True
|
248
243
|
continue
|
249
244
|
|
250
|
-
if _get_is_active(
|
245
|
+
if self._get_is_active(
|
246
|
+
request, tab_item.get("link_callback") or tab_item["link"]
|
247
|
+
):
|
251
248
|
has_tab_link_active = True
|
252
249
|
break
|
253
250
|
|
@@ -255,7 +252,7 @@ class UnfoldAdminSite(AdminSite):
|
|
255
252
|
item["active"] = True
|
256
253
|
|
257
254
|
if isinstance(item["link"], Callable):
|
258
|
-
item["
|
255
|
+
item["link_callback"] = lazy(item["link"])(request)
|
259
256
|
|
260
257
|
# Permission callback
|
261
258
|
item["has_permission"] = self._call_permission_callback(
|
@@ -290,8 +287,11 @@ class UnfoldAdminSite(AdminSite):
|
|
290
287
|
)
|
291
288
|
|
292
289
|
if isinstance(item["link"], Callable):
|
293
|
-
item["
|
290
|
+
item["link_callback"] = lazy(item["link"])(request)
|
294
291
|
|
292
|
+
item["active"] = self._get_is_active(
|
293
|
+
request, item.get("link_callback") or item["link"]
|
294
|
+
)
|
295
295
|
allowed_items.append(item)
|
296
296
|
|
297
297
|
tab["items"] = allowed_items
|
@@ -354,6 +354,19 @@ class UnfoldAdminSite(AdminSite):
|
|
354
354
|
|
355
355
|
return target
|
356
356
|
|
357
|
+
def _process_favicons(
|
358
|
+
self, request: HttpRequest, favicons: List[Dict]
|
359
|
+
) -> List[Favicon]:
|
360
|
+
return [
|
361
|
+
Favicon(
|
362
|
+
href=self._get_value(item["href"], request),
|
363
|
+
rel=item.get("rel"),
|
364
|
+
sizes=item.get("sizes"),
|
365
|
+
type=item.get("type"),
|
366
|
+
)
|
367
|
+
for item in favicons
|
368
|
+
]
|
369
|
+
|
357
370
|
def _process_colors(
|
358
371
|
self, colors: Dict[str, Dict[str, str]]
|
359
372
|
) -> Dict[str, Dict[str, str]]:
|
@@ -365,3 +378,14 @@ class UnfoldAdminSite(AdminSite):
|
|
365
378
|
colors[name][weight] = " ".join(str(item) for item in hex_to_rgb(value))
|
366
379
|
|
367
380
|
return colors
|
381
|
+
|
382
|
+
def _get_is_active(self, request: HttpRequest, link: str) -> bool:
|
383
|
+
if not isinstance(link, str):
|
384
|
+
link = str(link)
|
385
|
+
|
386
|
+
if link in request.path and link != reverse_lazy(f"{self.name}:index"):
|
387
|
+
return True
|
388
|
+
elif link == request.path == reverse_lazy(f"{self.name}:index"):
|
389
|
+
return True
|
390
|
+
|
391
|
+
return False
|