django-tabbed-admin3 3.0.0__tar.gz → 3.2.0__tar.gz
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-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/MANIFEST.in +1 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/PKG-INFO +35 -12
- django-tabbed-admin3-3.0.0/django_tabbed_admin3.egg-info/PKG-INFO → django_tabbed_admin3-3.2.0/README.rst +13 -21
- django-tabbed-admin3-3.0.0/README.rst → django_tabbed_admin3-3.2.0/django_tabbed_admin3.egg-info/PKG-INFO +44 -1
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/django_tabbed_admin3.egg-info/SOURCES.txt +2 -2
- django_tabbed_admin3-3.2.0/django_tabbed_admin3.egg-info/requires.txt +1 -0
- django_tabbed_admin3-3.2.0/pyproject.toml +57 -0
- django_tabbed_admin3-3.2.0/setup.cfg +4 -0
- django_tabbed_admin3-3.2.0/tabbed_admin/__init__.py +3 -0
- django_tabbed_admin3-3.2.0/tabbed_admin/admin.py +141 -0
- django_tabbed_admin3-3.2.0/tabbed_admin/settings.py +12 -0
- django_tabbed_admin3-3.2.0/tabbed_admin/templates/tabbed_admin/change_form.html +65 -0
- django_tabbed_admin3-3.2.0/tabbed_admin/templatetags/tabbed_admin_tags.py +58 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/tests/admin.py +5 -15
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/tests/models.py +8 -16
- django_tabbed_admin3-3.2.0/tabbed_admin/tests/runtests.py +50 -0
- django_tabbed_admin3-3.2.0/tabbed_admin/tests/tests.py +193 -0
- django_tabbed_admin3-3.2.0/tabbed_admin/tests/urls.py +8 -0
- django-tabbed-admin3-3.0.0/setup.cfg +0 -7
- django-tabbed-admin3-3.0.0/setup.py +0 -38
- django-tabbed-admin3-3.0.0/tabbed_admin/__init__.py +0 -6
- django-tabbed-admin3-3.0.0/tabbed_admin/admin.py +0 -150
- django-tabbed-admin3-3.0.0/tabbed_admin/settings.py +0 -18
- django-tabbed-admin3-3.0.0/tabbed_admin/templates/tabbed_admin/change_form.html +0 -104
- django-tabbed-admin3-3.0.0/tabbed_admin/templatetags/tabbed_admin_tags.py +0 -49
- django-tabbed-admin3-3.0.0/tabbed_admin/tests/runtests.py +0 -74
- django-tabbed-admin3-3.0.0/tabbed_admin/tests/tests.py +0 -240
- django-tabbed-admin3-3.0.0/tabbed_admin/tests/urls.py +0 -6
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/AUTHORS.txt +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/LICENSE.txt +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/django_tabbed_admin3.egg-info/dependency_links.txt +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/django_tabbed_admin3.egg-info/top_level.txt +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/models.py +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-bg_flat_0_eeeeee_40x100.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-bg_flat_55_c0402a_40x100.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-bg_flat_55_eeeeee_40x100.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-bg_glass_100_f8f8f8_1x400.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-bg_glass_35_dddddd_1x400.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-bg_glass_60_eeeeee_1x400.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-bg_inset-hard_75_999999_1x100.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-bg_inset-soft_50_c9c9c9_1x100.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-icons_3383bb_256x240.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-icons_444444_256x240.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-icons_454545_256x240.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-icons_555555_256x240.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-icons_70b2e1_256x240.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-icons_777620_256x240.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-icons_777777_256x240.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-icons_999999_256x240.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-icons_cc0000_256x240.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-icons_fbc856_256x240.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/images/ui-icons_ffffff_256x240.png +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/jquery-ui-1.11.4.min.css +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/tabbed_admin.css +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/tabbed_grappelli_admin.css +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/js/jquery-ui-1.11.4.min.js +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/templatetags/__init__.py +0 -0
- {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/tests/__init__.py +0 -0
|
@@ -1,28 +1,49 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: django-tabbed-admin3
|
|
3
|
-
Version: 3.
|
|
4
|
-
Summary: Easily add tabs to django admin forms.
|
|
5
|
-
Author: Michael Galler
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Platform: OS Independent
|
|
3
|
+
Version: 3.2.0
|
|
4
|
+
Summary: Easily add tabs to django admin forms. Django 6.1 compatible fork.
|
|
5
|
+
Author: Guillaume Pousseo (original), Michael Galler (django 4 fork)
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/mgaller/django-tabbed-admin3
|
|
9
8
|
Classifier: Development Status :: 5 - Production/Stable
|
|
10
9
|
Classifier: Environment :: Web Environment
|
|
11
10
|
Classifier: Framework :: Django
|
|
11
|
+
Classifier: Framework :: Django :: 4.2
|
|
12
|
+
Classifier: Framework :: Django :: 5.0
|
|
13
|
+
Classifier: Framework :: Django :: 5.1
|
|
14
|
+
Classifier: Framework :: Django :: 5.2
|
|
15
|
+
Classifier: Framework :: Django :: 6.0
|
|
16
|
+
Classifier: Framework :: Django :: 6.1
|
|
12
17
|
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
14
18
|
Classifier: Operating System :: OS Independent
|
|
15
|
-
Classifier: Programming Language :: Python
|
|
16
|
-
Classifier: Programming Language :: Python ::
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Description-Content-Type: text/x-rst
|
|
18
27
|
License-File: LICENSE.txt
|
|
19
28
|
License-File: AUTHORS.txt
|
|
29
|
+
Requires-Dist: django>=4.2
|
|
30
|
+
Dynamic: license-file
|
|
20
31
|
|
|
21
32
|
###################
|
|
22
33
|
Django tabbed admin
|
|
23
34
|
###################
|
|
24
35
|
|
|
25
|
-
This is a simple fork of django-tabbed-admin https://github.com/omji/django-tabbed-admin with Django
|
|
36
|
+
This is a simple fork of django-tabbed-admin https://github.com/omji/django-tabbed-admin with compatibility updates for modern Django versions.
|
|
37
|
+
|
|
38
|
+
Version 3.2 adds Django 6.1 support while retaining jQuery UI and the existing
|
|
39
|
+
``TABBED_ADMIN_*`` settings. Tests cover Django 4.2, 5.0, 5.1, 5.2, 6.0 and 6.1.
|
|
40
|
+
The change-form template now inherits Django's form controls, actions and popup
|
|
41
|
+
fields instead of copying them. Projects overriding
|
|
42
|
+
``tabbed_admin/change_form.html`` must update their override to use this fix.
|
|
43
|
+
For integration with another admin extension, ``TabbedModelAdmin.tabbed_admin_base_template``
|
|
44
|
+
can specify a parent template instead of ``admin/change_form.html``.
|
|
45
|
+
The ``tabbed_admin_base_template`` context variable takes precedence over this
|
|
46
|
+
attribute, allowing other admin extensions to supply the parent template.
|
|
26
47
|
|
|
27
48
|
|
|
28
49
|
Simple library to easilly add tabs to admin forms. It also allows users to re-order inlines and fieldsets.
|
|
@@ -198,6 +219,8 @@ Contribution
|
|
|
198
219
|
Please feel free to contribute. Any help and advices are much appreciated.
|
|
199
220
|
You will find an example application to run and develop the library easily.
|
|
200
221
|
|
|
222
|
+
Run the standalone test suite with ``PYTHONPATH=. python tabbed_admin/tests/runtests.py``.
|
|
223
|
+
|
|
201
224
|
|
|
202
225
|
*****
|
|
203
226
|
LINKS
|
|
@@ -1,28 +1,18 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: django-tabbed-admin3
|
|
3
|
-
Version: 3.0.0
|
|
4
|
-
Summary: Easily add tabs to django admin forms.
|
|
5
|
-
Author: Michael Galler
|
|
6
|
-
Author-email: mgaller@live.de
|
|
7
|
-
License: BSD License
|
|
8
|
-
Platform: OS Independent
|
|
9
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
-
Classifier: Environment :: Web Environment
|
|
11
|
-
Classifier: Framework :: Django
|
|
12
|
-
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
14
|
-
Classifier: Operating System :: OS Independent
|
|
15
|
-
Classifier: Programming Language :: Python
|
|
16
|
-
Classifier: Programming Language :: Python :: 2.7
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.5
|
|
18
|
-
License-File: LICENSE.txt
|
|
19
|
-
License-File: AUTHORS.txt
|
|
20
|
-
|
|
21
1
|
###################
|
|
22
2
|
Django tabbed admin
|
|
23
3
|
###################
|
|
24
4
|
|
|
25
|
-
This is a simple fork of django-tabbed-admin https://github.com/omji/django-tabbed-admin with Django
|
|
5
|
+
This is a simple fork of django-tabbed-admin https://github.com/omji/django-tabbed-admin with compatibility updates for modern Django versions.
|
|
6
|
+
|
|
7
|
+
Version 3.2 adds Django 6.1 support while retaining jQuery UI and the existing
|
|
8
|
+
``TABBED_ADMIN_*`` settings. Tests cover Django 4.2, 5.0, 5.1, 5.2, 6.0 and 6.1.
|
|
9
|
+
The change-form template now inherits Django's form controls, actions and popup
|
|
10
|
+
fields instead of copying them. Projects overriding
|
|
11
|
+
``tabbed_admin/change_form.html`` must update their override to use this fix.
|
|
12
|
+
For integration with another admin extension, ``TabbedModelAdmin.tabbed_admin_base_template``
|
|
13
|
+
can specify a parent template instead of ``admin/change_form.html``.
|
|
14
|
+
The ``tabbed_admin_base_template`` context variable takes precedence over this
|
|
15
|
+
attribute, allowing other admin extensions to supply the parent template.
|
|
26
16
|
|
|
27
17
|
|
|
28
18
|
Simple library to easilly add tabs to admin forms. It also allows users to re-order inlines and fieldsets.
|
|
@@ -198,6 +188,8 @@ Contribution
|
|
|
198
188
|
Please feel free to contribute. Any help and advices are much appreciated.
|
|
199
189
|
You will find an example application to run and develop the library easily.
|
|
200
190
|
|
|
191
|
+
Run the standalone test suite with ``PYTHONPATH=. python tabbed_admin/tests/runtests.py``.
|
|
192
|
+
|
|
201
193
|
|
|
202
194
|
*****
|
|
203
195
|
LINKS
|
|
@@ -1,8 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-tabbed-admin3
|
|
3
|
+
Version: 3.2.0
|
|
4
|
+
Summary: Easily add tabs to django admin forms. Django 6.1 compatible fork.
|
|
5
|
+
Author: Guillaume Pousseo (original), Michael Galler (django 4 fork)
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/mgaller/django-tabbed-admin3
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Environment :: Web Environment
|
|
10
|
+
Classifier: Framework :: Django
|
|
11
|
+
Classifier: Framework :: Django :: 4.2
|
|
12
|
+
Classifier: Framework :: Django :: 5.0
|
|
13
|
+
Classifier: Framework :: Django :: 5.1
|
|
14
|
+
Classifier: Framework :: Django :: 5.2
|
|
15
|
+
Classifier: Framework :: Django :: 6.0
|
|
16
|
+
Classifier: Framework :: Django :: 6.1
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Description-Content-Type: text/x-rst
|
|
27
|
+
License-File: LICENSE.txt
|
|
28
|
+
License-File: AUTHORS.txt
|
|
29
|
+
Requires-Dist: django>=4.2
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
1
32
|
###################
|
|
2
33
|
Django tabbed admin
|
|
3
34
|
###################
|
|
4
35
|
|
|
5
|
-
This is a simple fork of django-tabbed-admin https://github.com/omji/django-tabbed-admin with Django
|
|
36
|
+
This is a simple fork of django-tabbed-admin https://github.com/omji/django-tabbed-admin with compatibility updates for modern Django versions.
|
|
37
|
+
|
|
38
|
+
Version 3.2 adds Django 6.1 support while retaining jQuery UI and the existing
|
|
39
|
+
``TABBED_ADMIN_*`` settings. Tests cover Django 4.2, 5.0, 5.1, 5.2, 6.0 and 6.1.
|
|
40
|
+
The change-form template now inherits Django's form controls, actions and popup
|
|
41
|
+
fields instead of copying them. Projects overriding
|
|
42
|
+
``tabbed_admin/change_form.html`` must update their override to use this fix.
|
|
43
|
+
For integration with another admin extension, ``TabbedModelAdmin.tabbed_admin_base_template``
|
|
44
|
+
can specify a parent template instead of ``admin/change_form.html``.
|
|
45
|
+
The ``tabbed_admin_base_template`` context variable takes precedence over this
|
|
46
|
+
attribute, allowing other admin extensions to supply the parent template.
|
|
6
47
|
|
|
7
48
|
|
|
8
49
|
Simple library to easilly add tabs to admin forms. It also allows users to re-order inlines and fieldsets.
|
|
@@ -178,6 +219,8 @@ Contribution
|
|
|
178
219
|
Please feel free to contribute. Any help and advices are much appreciated.
|
|
179
220
|
You will find an example application to run and develop the library easily.
|
|
180
221
|
|
|
222
|
+
Run the standalone test suite with ``PYTHONPATH=. python tabbed_admin/tests/runtests.py``.
|
|
223
|
+
|
|
181
224
|
|
|
182
225
|
*****
|
|
183
226
|
LINKS
|
{django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/django_tabbed_admin3.egg-info/SOURCES.txt
RENAMED
|
@@ -2,11 +2,11 @@ AUTHORS.txt
|
|
|
2
2
|
LICENSE.txt
|
|
3
3
|
MANIFEST.in
|
|
4
4
|
README.rst
|
|
5
|
-
|
|
6
|
-
setup.py
|
|
5
|
+
pyproject.toml
|
|
7
6
|
django_tabbed_admin3.egg-info/PKG-INFO
|
|
8
7
|
django_tabbed_admin3.egg-info/SOURCES.txt
|
|
9
8
|
django_tabbed_admin3.egg-info/dependency_links.txt
|
|
9
|
+
django_tabbed_admin3.egg-info/requires.txt
|
|
10
10
|
django_tabbed_admin3.egg-info/top_level.txt
|
|
11
11
|
tabbed_admin/__init__.py
|
|
12
12
|
tabbed_admin/admin.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
django>=4.2
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "django-tabbed-admin3"
|
|
7
|
+
version = "3.2.0"
|
|
8
|
+
description = "Easily add tabs to django admin forms. Django 6.1 compatible fork."
|
|
9
|
+
readme = "README.rst"
|
|
10
|
+
license = "BSD-3-Clause"
|
|
11
|
+
license-files = ["LICENSE.txt", "AUTHORS.txt"]
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Guillaume Pousseo (original)"},
|
|
14
|
+
{name = "Michael Galler (django 4 fork)"},
|
|
15
|
+
]
|
|
16
|
+
requires-python = ">=3.10"
|
|
17
|
+
dependencies = ["django>=4.2"]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 5 - Production/Stable",
|
|
20
|
+
"Environment :: Web Environment",
|
|
21
|
+
"Framework :: Django",
|
|
22
|
+
"Framework :: Django :: 4.2",
|
|
23
|
+
"Framework :: Django :: 5.0",
|
|
24
|
+
"Framework :: Django :: 5.1",
|
|
25
|
+
"Framework :: Django :: 5.2",
|
|
26
|
+
"Framework :: Django :: 6.0",
|
|
27
|
+
"Framework :: Django :: 6.1",
|
|
28
|
+
"Intended Audience :: Developers",
|
|
29
|
+
"Operating System :: OS Independent",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3.10",
|
|
32
|
+
"Programming Language :: Python :: 3.11",
|
|
33
|
+
"Programming Language :: Python :: 3.12",
|
|
34
|
+
"Programming Language :: Python :: 3.13",
|
|
35
|
+
"Programming Language :: Python :: 3.14",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/mgaller/django-tabbed-admin3"
|
|
40
|
+
|
|
41
|
+
[tool.setuptools.packages.find]
|
|
42
|
+
include = ["tabbed_admin*"]
|
|
43
|
+
namespaces = false
|
|
44
|
+
|
|
45
|
+
[tool.setuptools]
|
|
46
|
+
include-package-data = false
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.package-data]
|
|
49
|
+
tabbed_admin = ["templates/**/*.html", "static/**/*.css", "static/**/*.js", "static/**/*.png"]
|
|
50
|
+
|
|
51
|
+
[tool.ruff]
|
|
52
|
+
target-version = "py310"
|
|
53
|
+
line-length = 120
|
|
54
|
+
|
|
55
|
+
[tool.ruff.lint]
|
|
56
|
+
select = ["E", "F", "I"]
|
|
57
|
+
ignore = ["E501"]
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.contrib.admin.options import ModelAdmin
|
|
3
|
+
from django.utils.translation import gettext_lazy as _
|
|
4
|
+
|
|
5
|
+
from .settings import JQUERY_UI_CSS, JQUERY_UI_JS, USE_JQUERY_UI
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TabbedModelAdmin(ModelAdmin):
|
|
9
|
+
tabs = None
|
|
10
|
+
formatted_tabs = {}
|
|
11
|
+
|
|
12
|
+
# Needs a specific template to display the tabs
|
|
13
|
+
change_form_template = "tabbed_admin/change_form.html"
|
|
14
|
+
tabbed_admin_base_template = "admin/change_form.html"
|
|
15
|
+
|
|
16
|
+
def get_fieldsets(self, request, obj=None):
|
|
17
|
+
"""
|
|
18
|
+
Overwrites BaseModelAdmin fieldsets to add fieldsets passed by the
|
|
19
|
+
tabs.
|
|
20
|
+
If the tabs attribute is not set, use the default ModelAdmin method.
|
|
21
|
+
"""
|
|
22
|
+
tabs_fieldsets = self.get_formatted_tabs(request, obj)["fieldsets"]
|
|
23
|
+
if self.tabs is not None:
|
|
24
|
+
self.fieldsets = ()
|
|
25
|
+
self.fieldsets = self.add_tabbed_item(tabs_fieldsets, self.fieldsets)
|
|
26
|
+
return super().get_fieldsets(request, obj)
|
|
27
|
+
|
|
28
|
+
def get_inline_instances(self, request, obj=None):
|
|
29
|
+
"""
|
|
30
|
+
Overwrites BaseModelAdmin inline_instances to add inlines passed by
|
|
31
|
+
the tabs.
|
|
32
|
+
If the tabs attribute is not set, use the default ModelAdmin method.
|
|
33
|
+
"""
|
|
34
|
+
if self.tabs is not None:
|
|
35
|
+
self.inlines = ()
|
|
36
|
+
tabs_inlines = self.get_formatted_tabs(request, obj)["inlines"]
|
|
37
|
+
self.inlines = self.add_tabbed_item(tabs_inlines, self.inlines)
|
|
38
|
+
return super().get_inline_instances(request, obj)
|
|
39
|
+
|
|
40
|
+
def add_tabbed_item(self, items_to_add, collection):
|
|
41
|
+
"""
|
|
42
|
+
Adds tabbed items (inlines or fieldsets) to their corresponding
|
|
43
|
+
attribute.
|
|
44
|
+
"""
|
|
45
|
+
if items_to_add:
|
|
46
|
+
for item in items_to_add:
|
|
47
|
+
if item not in collection:
|
|
48
|
+
if isinstance(collection, tuple):
|
|
49
|
+
collection = collection + (item,)
|
|
50
|
+
elif isinstance(collection, list):
|
|
51
|
+
collection.append(item)
|
|
52
|
+
return collection
|
|
53
|
+
|
|
54
|
+
def get_tabs(self, request, obj=None):
|
|
55
|
+
"""
|
|
56
|
+
Returns the tabs attribute.
|
|
57
|
+
"""
|
|
58
|
+
return self.tabs
|
|
59
|
+
|
|
60
|
+
def get_formatted_tabs(self, request, obj=None):
|
|
61
|
+
"""
|
|
62
|
+
Returns the formatted tabs attribute.
|
|
63
|
+
"""
|
|
64
|
+
if self.tabs:
|
|
65
|
+
self.parse_fieldsets_inlines_from_tabs(request, obj)
|
|
66
|
+
else:
|
|
67
|
+
self.formatted_tabs = {"fields": [], "fieldsets": (), "inlines": ()}
|
|
68
|
+
return self.formatted_tabs
|
|
69
|
+
|
|
70
|
+
def parse_fieldsets_inlines_from_tabs(self, request, obj=None):
|
|
71
|
+
"""
|
|
72
|
+
Parses the self.tabs attribute. Checks its integrity and populates
|
|
73
|
+
self._tabs_fieldsets and self._tabs_inlines attributes.
|
|
74
|
+
"""
|
|
75
|
+
tabs_fieldsets = ()
|
|
76
|
+
tabs_inlines = ()
|
|
77
|
+
self.formatted_tabs = {"fields": []}
|
|
78
|
+
for tab in self.get_tabs(request, obj):
|
|
79
|
+
# Checks that each tab is formatted with 2 arguments: verbose name
|
|
80
|
+
# of the tab and the tab configuration.
|
|
81
|
+
if not isinstance(tab, (tuple, list)):
|
|
82
|
+
raise TypeError(_("Each tab entry must be either a list or a tuple"))
|
|
83
|
+
if len(tab) != 2:
|
|
84
|
+
raise ValueError(_("Each tabs entry must contain 2 arguments: a verbose name and the tab setup."))
|
|
85
|
+
if not isinstance(tab[1], (tuple, list)):
|
|
86
|
+
raise TypeError(_("A tab definition must be either a list or a tuple"))
|
|
87
|
+
# Parse the tab configuration: if an entry is a tuple or a list
|
|
88
|
+
# it's a fieldset, otherwise it's an inline class.
|
|
89
|
+
formatted_tab = {"name": tab[0], "entries": []}
|
|
90
|
+
for tab_entry in tab[1]:
|
|
91
|
+
if isinstance(tab_entry, (tuple, list)):
|
|
92
|
+
tabs_fieldsets = tabs_fieldsets + (tab_entry,)
|
|
93
|
+
formatted_tab_entry = {
|
|
94
|
+
"type": "fieldset",
|
|
95
|
+
"name": tab_entry[0],
|
|
96
|
+
"config": tab_entry[1],
|
|
97
|
+
"index": len(tabs_fieldsets) - 1,
|
|
98
|
+
}
|
|
99
|
+
else:
|
|
100
|
+
tabs_inlines = tabs_inlines + (tab_entry,)
|
|
101
|
+
formatted_tab_entry = {
|
|
102
|
+
"type": "inline",
|
|
103
|
+
"name": tab_entry.__name__,
|
|
104
|
+
}
|
|
105
|
+
formatted_tab["entries"].append(formatted_tab_entry)
|
|
106
|
+
self.formatted_tabs["fields"].append(formatted_tab)
|
|
107
|
+
self.formatted_tabs["fieldsets"] = tabs_fieldsets
|
|
108
|
+
self.formatted_tabs["inlines"] = tabs_inlines
|
|
109
|
+
|
|
110
|
+
def render_change_form(self, request, context, add=False, change=False, form_url="", obj=None):
|
|
111
|
+
context = {
|
|
112
|
+
**context,
|
|
113
|
+
"tabs": self.get_formatted_tabs(request, obj),
|
|
114
|
+
"tabbed_admin_base_template": context.get("tabbed_admin_base_template", self.tabbed_admin_base_template),
|
|
115
|
+
}
|
|
116
|
+
return super().render_change_form(
|
|
117
|
+
request,
|
|
118
|
+
context,
|
|
119
|
+
add=add,
|
|
120
|
+
change=change,
|
|
121
|
+
form_url=form_url,
|
|
122
|
+
obj=obj,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
class Media:
|
|
126
|
+
"""
|
|
127
|
+
Extends media class to add custom jquery ui if
|
|
128
|
+
TABBED_ADMIN_USE_JQUERY_UI is set to True.
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
if "grappelli" in settings.INSTALLED_APPS:
|
|
132
|
+
css = {"all": ("tabbed_admin/css/tabbed_grappelli_admin.css",)}
|
|
133
|
+
|
|
134
|
+
if USE_JQUERY_UI:
|
|
135
|
+
css = {
|
|
136
|
+
"all": (
|
|
137
|
+
JQUERY_UI_CSS,
|
|
138
|
+
"tabbed_admin/css/tabbed_admin.css",
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
js = (JQUERY_UI_JS,)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
|
|
3
|
+
# Activate the library jquery ui
|
|
4
|
+
USE_JQUERY_UI = getattr(settings, "TABBED_ADMIN_USE_JQUERY_UI", False)
|
|
5
|
+
|
|
6
|
+
# Default jquery ui css and js
|
|
7
|
+
DEFAULT_JQUERY_UI_CSS = "tabbed_admin/css/jquery-ui-1.11.4.min.css"
|
|
8
|
+
DEFAULT_JQUERY_UI_JS = "tabbed_admin/js/jquery-ui-1.11.4.min.js"
|
|
9
|
+
|
|
10
|
+
# User ability to override the default css and js
|
|
11
|
+
JQUERY_UI_CSS = getattr(settings, "TABBED_ADMIN_JQUERY_UI_CSS", DEFAULT_JQUERY_UI_CSS)
|
|
12
|
+
JQUERY_UI_JS = getattr(settings, "TABBED_ADMIN_JQUERY_UI_JS", DEFAULT_JQUERY_UI_JS)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{% extends tabbed_admin_base_template|default:"admin/change_form.html" %}
|
|
2
|
+
{% load tabbed_admin_tags %}
|
|
3
|
+
|
|
4
|
+
{% block field_sets %}
|
|
5
|
+
{% if tabs.fields %}
|
|
6
|
+
<div id="tabs">
|
|
7
|
+
<ul>
|
|
8
|
+
{% for tab in tabs.fields %}
|
|
9
|
+
<li><a href="#tabs-{{ forloop.counter }}" id="for_tabs-{{ forloop.counter }}">{{ tab.name }}</a></li>
|
|
10
|
+
{% endfor %}
|
|
11
|
+
</ul>
|
|
12
|
+
{% for tab in tabs.fields %}
|
|
13
|
+
<div id="tabs-{{ forloop.counter }}" class="{{ tab.name }}">
|
|
14
|
+
{% for entry in tab.entries %}
|
|
15
|
+
{% render_tab_fieldsets_inlines entry %}
|
|
16
|
+
{% endfor %}
|
|
17
|
+
</div>
|
|
18
|
+
{% endfor %}
|
|
19
|
+
</div>
|
|
20
|
+
<script{% if csp_nonce %} nonce="{{ csp_nonce }}"{% endif %}>
|
|
21
|
+
(function($) {
|
|
22
|
+
$('#tabs').tabs({
|
|
23
|
+
{% if add %}active: false{% endif %}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const enabled_tabs = [];
|
|
27
|
+
const disabled_tabs = [];
|
|
28
|
+
{% for tab in page_config %}
|
|
29
|
+
{% if tab.enabled %}
|
|
30
|
+
enabled_tabs.push({{ forloop.counter0 }});
|
|
31
|
+
{% else %}
|
|
32
|
+
disabled_tabs.push({{ forloop.counter0 }});
|
|
33
|
+
{% endif %}
|
|
34
|
+
{% endfor %}
|
|
35
|
+
for (let i = 0; i < disabled_tabs.length; i++) {
|
|
36
|
+
$('#tabs').tabs('disable', disabled_tabs[i]);
|
|
37
|
+
}
|
|
38
|
+
{% if add %}
|
|
39
|
+
$('#tabs').tabs('option', 'active', enabled_tabs.length ? enabled_tabs[0] : {% if page_config %}false{% else %}0{% endif %});
|
|
40
|
+
{% endif %}
|
|
41
|
+
|
|
42
|
+
$('#tabs > div').each(function() {
|
|
43
|
+
if ($(this).find('.errorlist').length) {
|
|
44
|
+
$('#tabs #for_' + this.id).addClass('errortab');
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
$('#tabs').on('tabsactivate', function(event, ui) {
|
|
48
|
+
const scrollPos = $(window).scrollTop();
|
|
49
|
+
const hash = ui.newTab.find('a').attr('href');
|
|
50
|
+
if (hash) window.location.hash = hash;
|
|
51
|
+
$(window).scrollTop(scrollPos);
|
|
52
|
+
});
|
|
53
|
+
if ($('.errornote').length) {
|
|
54
|
+
$('.errornote').addClass('tabbed-errornote');
|
|
55
|
+
}
|
|
56
|
+
})(django.jQuery);
|
|
57
|
+
</script>
|
|
58
|
+
{% else %}
|
|
59
|
+
{{ block.super }}
|
|
60
|
+
{% endif %}
|
|
61
|
+
{% endblock %}
|
|
62
|
+
|
|
63
|
+
{% block inline_field_sets %}
|
|
64
|
+
{% if not tabs.fields %}{{ block.super }}{% endif %}
|
|
65
|
+
{% endblock %}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from django import template
|
|
2
|
+
from django.contrib.admin.helpers import Fieldset
|
|
3
|
+
from django.core.exceptions import ImproperlyConfigured
|
|
4
|
+
from django.template.loader import render_to_string
|
|
5
|
+
|
|
6
|
+
register = template.Library()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@register.simple_tag(takes_context=True)
|
|
10
|
+
def render_tab_fieldsets_inlines(context, entry):
|
|
11
|
+
"""
|
|
12
|
+
Render the fieldsets and inlines for a tab.
|
|
13
|
+
"""
|
|
14
|
+
fieldset_template = "admin/includes/fieldset.html"
|
|
15
|
+
admin_form = context["adminform"]
|
|
16
|
+
if "request" not in context:
|
|
17
|
+
raise ImproperlyConfigured(
|
|
18
|
+
'"request" missing from context. Add '
|
|
19
|
+
'"django.template.context_processors.request" to your '
|
|
20
|
+
"TEMPLATES context_processors setting."
|
|
21
|
+
)
|
|
22
|
+
request = context["request"]
|
|
23
|
+
readonly_fields = admin_form.readonly_fields
|
|
24
|
+
values = context.flatten()
|
|
25
|
+
inline_matching = {}
|
|
26
|
+
if "inline_admin_formsets" in context:
|
|
27
|
+
inline_matching = {inline.opts.__class__.__name__: inline for inline in context["inline_admin_formsets"]}
|
|
28
|
+
|
|
29
|
+
if entry["type"] == "fieldset":
|
|
30
|
+
name = entry["name"]
|
|
31
|
+
f = Fieldset(
|
|
32
|
+
admin_form.form,
|
|
33
|
+
name,
|
|
34
|
+
readonly_fields=readonly_fields,
|
|
35
|
+
model_admin=admin_form.model_admin,
|
|
36
|
+
**entry["config"],
|
|
37
|
+
)
|
|
38
|
+
values.update(
|
|
39
|
+
fieldset=f,
|
|
40
|
+
heading_level=2,
|
|
41
|
+
prefix="fieldset",
|
|
42
|
+
id_prefix=0,
|
|
43
|
+
id_suffix=entry.get("index", 0),
|
|
44
|
+
)
|
|
45
|
+
return render_to_string(fieldset_template, values, request=request)
|
|
46
|
+
elif entry["type"] == "inline":
|
|
47
|
+
try:
|
|
48
|
+
inline_admin_formset = inline_matching[entry["name"]]
|
|
49
|
+
values["inline_admin_formset"] = inline_admin_formset
|
|
50
|
+
return render_to_string(
|
|
51
|
+
inline_admin_formset.opts.template,
|
|
52
|
+
values,
|
|
53
|
+
request=request,
|
|
54
|
+
)
|
|
55
|
+
except KeyError:
|
|
56
|
+
# The user does not have the permission
|
|
57
|
+
pass
|
|
58
|
+
return ""
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from django.contrib import admin
|
|
2
2
|
|
|
3
3
|
from tabbed_admin import TabbedModelAdmin
|
|
4
|
-
from tabbed_admin.tests.models import
|
|
4
|
+
from tabbed_admin.tests.models import Album, Band, Concert, Interview, Musician
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class MusicianInline(admin.StackedInline):
|
|
@@ -24,19 +24,9 @@ class BandAdmin(TabbedModelAdmin):
|
|
|
24
24
|
model = Band
|
|
25
25
|
|
|
26
26
|
tab_overview = (
|
|
27
|
-
(None, {
|
|
28
|
-
'fields': ('name', 'bio', 'style')
|
|
29
|
-
}),
|
|
27
|
+
(None, {"fields": ("name", "bio", "style")}),
|
|
30
28
|
MusicianInline,
|
|
31
|
-
(
|
|
32
|
-
'fields': ('agent', 'phone', 'email')
|
|
33
|
-
})
|
|
29
|
+
("Contact", {"fields": ("agent", "phone", "email")}),
|
|
34
30
|
)
|
|
35
|
-
tab_ressources = (
|
|
36
|
-
|
|
37
|
-
AlbumInline
|
|
38
|
-
)
|
|
39
|
-
tabs = [
|
|
40
|
-
('Overview', tab_overview),
|
|
41
|
-
('Ressources', tab_ressources)
|
|
42
|
-
]
|
|
31
|
+
tab_ressources = (ConcertInline, AlbumInline)
|
|
32
|
+
tabs = [("Overview", tab_overview), ("Ressources", tab_ressources)]
|
|
@@ -10,12 +10,9 @@ class Band(models.Model):
|
|
|
10
10
|
|
|
11
11
|
name = models.CharField(max_length=100)
|
|
12
12
|
bio = models.TextField(blank=True, null=True)
|
|
13
|
-
style = models.
|
|
14
|
-
(STYLE_ROCK,
|
|
15
|
-
|
|
16
|
-
(STYLE_JAZZ, 'Jazz'),
|
|
17
|
-
(STYLE_OVERRIDE, 'Override')
|
|
18
|
-
))
|
|
13
|
+
style = models.PositiveSmallIntegerField(
|
|
14
|
+
choices=((STYLE_ROCK, "Rock"), (STYLE_FUNK, "Funk"), (STYLE_JAZZ, "Jazz"), (STYLE_OVERRIDE, "Override"))
|
|
15
|
+
)
|
|
19
16
|
agent = models.CharField(max_length=100, blank=True, null=True)
|
|
20
17
|
phone = models.CharField(max_length=100, blank=True, null=True)
|
|
21
18
|
email = models.CharField(max_length=100, blank=True, null=True)
|
|
@@ -26,30 +23,25 @@ class Band(models.Model):
|
|
|
26
23
|
|
|
27
24
|
|
|
28
25
|
class Musician(models.Model):
|
|
29
|
-
band = models.ForeignKey(Band)
|
|
26
|
+
band = models.ForeignKey(Band, on_delete=models.CASCADE)
|
|
30
27
|
first_name = models.CharField(max_length=100)
|
|
31
28
|
last_name = models.CharField(max_length=100)
|
|
32
|
-
specialty = models.
|
|
33
|
-
(1, 'Vocal'),
|
|
34
|
-
(2, 'Guitar'),
|
|
35
|
-
(3, 'Bass'),
|
|
36
|
-
(4, 'Drums')
|
|
37
|
-
))
|
|
29
|
+
specialty = models.PositiveSmallIntegerField(choices=((1, "Vocal"), (2, "Guitar"), (3, "Bass"), (4, "Drums")))
|
|
38
30
|
|
|
39
31
|
|
|
40
32
|
class Concert(models.Model):
|
|
41
|
-
band = models.ForeignKey(Band)
|
|
33
|
+
band = models.ForeignKey(Band, on_delete=models.CASCADE)
|
|
42
34
|
location = models.CharField(max_length=100)
|
|
43
35
|
date = models.DateField()
|
|
44
36
|
|
|
45
37
|
|
|
46
38
|
class Album(models.Model):
|
|
47
|
-
band = models.ForeignKey(Band)
|
|
39
|
+
band = models.ForeignKey(Band, on_delete=models.CASCADE)
|
|
48
40
|
name = models.CharField(max_length=100)
|
|
49
41
|
date = models.DateField()
|
|
50
42
|
|
|
51
43
|
|
|
52
44
|
class Interview(models.Model):
|
|
53
|
-
band = models.ForeignKey(Band)
|
|
45
|
+
band = models.ForeignKey(Band, on_delete=models.CASCADE)
|
|
54
46
|
media_name = models.CharField(max_length=100)
|
|
55
47
|
date = models.DateField()
|