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.
Files changed (59) hide show
  1. {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/MANIFEST.in +1 -0
  2. {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/PKG-INFO +35 -12
  3. django-tabbed-admin3-3.0.0/django_tabbed_admin3.egg-info/PKG-INFO → django_tabbed_admin3-3.2.0/README.rst +13 -21
  4. django-tabbed-admin3-3.0.0/README.rst → django_tabbed_admin3-3.2.0/django_tabbed_admin3.egg-info/PKG-INFO +44 -1
  5. {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/django_tabbed_admin3.egg-info/SOURCES.txt +2 -2
  6. django_tabbed_admin3-3.2.0/django_tabbed_admin3.egg-info/requires.txt +1 -0
  7. django_tabbed_admin3-3.2.0/pyproject.toml +57 -0
  8. django_tabbed_admin3-3.2.0/setup.cfg +4 -0
  9. django_tabbed_admin3-3.2.0/tabbed_admin/__init__.py +3 -0
  10. django_tabbed_admin3-3.2.0/tabbed_admin/admin.py +141 -0
  11. django_tabbed_admin3-3.2.0/tabbed_admin/settings.py +12 -0
  12. django_tabbed_admin3-3.2.0/tabbed_admin/templates/tabbed_admin/change_form.html +65 -0
  13. django_tabbed_admin3-3.2.0/tabbed_admin/templatetags/tabbed_admin_tags.py +58 -0
  14. {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/tests/admin.py +5 -15
  15. {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/tests/models.py +8 -16
  16. django_tabbed_admin3-3.2.0/tabbed_admin/tests/runtests.py +50 -0
  17. django_tabbed_admin3-3.2.0/tabbed_admin/tests/tests.py +193 -0
  18. django_tabbed_admin3-3.2.0/tabbed_admin/tests/urls.py +8 -0
  19. django-tabbed-admin3-3.0.0/setup.cfg +0 -7
  20. django-tabbed-admin3-3.0.0/setup.py +0 -38
  21. django-tabbed-admin3-3.0.0/tabbed_admin/__init__.py +0 -6
  22. django-tabbed-admin3-3.0.0/tabbed_admin/admin.py +0 -150
  23. django-tabbed-admin3-3.0.0/tabbed_admin/settings.py +0 -18
  24. django-tabbed-admin3-3.0.0/tabbed_admin/templates/tabbed_admin/change_form.html +0 -104
  25. django-tabbed-admin3-3.0.0/tabbed_admin/templatetags/tabbed_admin_tags.py +0 -49
  26. django-tabbed-admin3-3.0.0/tabbed_admin/tests/runtests.py +0 -74
  27. django-tabbed-admin3-3.0.0/tabbed_admin/tests/tests.py +0 -240
  28. django-tabbed-admin3-3.0.0/tabbed_admin/tests/urls.py +0 -6
  29. {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/AUTHORS.txt +0 -0
  30. {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/LICENSE.txt +0 -0
  31. {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/django_tabbed_admin3.egg-info/dependency_links.txt +0 -0
  32. {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/django_tabbed_admin3.egg-info/top_level.txt +0 -0
  33. {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/models.py +0 -0
  34. {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
  35. {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
  36. {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
  37. {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
  38. {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
  39. {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
  40. {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
  41. {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
  42. {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
  43. {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
  44. {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
  45. {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
  46. {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
  47. {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
  48. {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
  49. {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
  50. {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
  51. {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
  52. {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
  53. {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
  54. {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
  55. {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/tabbed_admin.css +0 -0
  56. {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/static/tabbed_admin/css/tabbed_grappelli_admin.css +0 -0
  57. {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
  58. {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/templatetags/__init__.py +0 -0
  59. {django-tabbed-admin3-3.0.0 → django_tabbed_admin3-3.2.0}/tabbed_admin/tests/__init__.py +0 -0
@@ -3,3 +3,4 @@ include LICENSE.txt
3
3
  include README.rst
4
4
  recursive-include tabbed_admin/static *
5
5
  recursive-include tabbed_admin/templates *
6
+ prune tabbed_admin_exemple
@@ -1,28 +1,49 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
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
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 :: 2.7
17
- Classifier: Programming Language :: Python :: 3.5
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 4.x fix.
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 4.x fix.
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 4.x fix.
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
@@ -2,11 +2,11 @@ AUTHORS.txt
2
2
  LICENSE.txt
3
3
  MANIFEST.in
4
4
  README.rst
5
- setup.cfg
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,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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ from .admin import TabbedModelAdmin
2
+
3
+ __all__ = ["TabbedModelAdmin"]
@@ -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 Band, Musician, Concert, Album, Interview
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
- ('Contact', {
32
- 'fields': ('agent', 'phone', 'email')
33
- })
29
+ ("Contact", {"fields": ("agent", "phone", "email")}),
34
30
  )
35
- tab_ressources = (
36
- ConcertInline,
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.CharField(max_length=100, choices=(
14
- (STYLE_ROCK, 'Rock'),
15
- (STYLE_FUNK, 'Funk'),
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.CharField(max_length=100, choices=(
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()