django-admin-tables 0.1.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 (126) hide show
  1. django_admin_tables-0.1.0/.github/workflows/compat-matrix.yml +27 -0
  2. django_admin_tables-0.1.0/.github/workflows/js-tests.yml +24 -0
  3. django_admin_tables-0.1.0/.github/workflows/pre-commit.yml +28 -0
  4. django_admin_tables-0.1.0/.github/workflows/pytest.yml +31 -0
  5. django_admin_tables-0.1.0/.github/workflows/release.yml +55 -0
  6. django_admin_tables-0.1.0/.gitignore +42 -0
  7. django_admin_tables-0.1.0/.pre-commit-config.yaml +58 -0
  8. django_admin_tables-0.1.0/CHANGELOG.md +56 -0
  9. django_admin_tables-0.1.0/CONTRIBUTING.md +113 -0
  10. django_admin_tables-0.1.0/LICENSE +21 -0
  11. django_admin_tables-0.1.0/PKG-INFO +542 -0
  12. django_admin_tables-0.1.0/README.md +497 -0
  13. django_admin_tables-0.1.0/biome.json +19 -0
  14. django_admin_tables-0.1.0/docs/screenshots/changelist-radio-select.png +0 -0
  15. django_admin_tables-0.1.0/docs/screenshots/fk-links-changelist-before.png +0 -0
  16. django_admin_tables-0.1.0/docs/screenshots/fk-links-changelist.png +0 -0
  17. django_admin_tables-0.1.0/docs/screenshots/footer-rows-inline.png +0 -0
  18. django_admin_tables-0.1.0/docs/screenshots/footer-rows.png +0 -0
  19. django_admin_tables-0.1.0/docs/screenshots/hideable-columns.png +0 -0
  20. django_admin_tables-0.1.0/docs/screenshots/inline-radio-select-after-click.png +0 -0
  21. django_admin_tables-0.1.0/docs/screenshots/inline-radio-select.png +0 -0
  22. django_admin_tables-0.1.0/docs/screenshots/responsive-columns.png +0 -0
  23. django_admin_tables-0.1.0/docs/screenshots/row-actions-inline.png +0 -0
  24. django_admin_tables-0.1.0/docs/screenshots/row-actions.png +0 -0
  25. django_admin_tables-0.1.0/docs/themes/unfold/fk-links-changelist-dark.png +0 -0
  26. django_admin_tables-0.1.0/docs/themes/unfold/fk-links-changelist.png +0 -0
  27. django_admin_tables-0.1.0/docs/themes/unfold/footer-rows-inline-dark.png +0 -0
  28. django_admin_tables-0.1.0/docs/themes/unfold/footer-rows.png +0 -0
  29. django_admin_tables-0.1.0/docs/themes/unfold/hideable-columns-dark.png +0 -0
  30. django_admin_tables-0.1.0/docs/themes/unfold/hideable-columns.png +0 -0
  31. django_admin_tables-0.1.0/docs/themes/unfold/radio-select-changelist-dark.png +0 -0
  32. django_admin_tables-0.1.0/docs/themes/unfold/radio-select-inline.png +0 -0
  33. django_admin_tables-0.1.0/docs/themes/unfold/responsive-columns.png +0 -0
  34. django_admin_tables-0.1.0/docs/themes/unfold/row-actions-inline-dark.png +0 -0
  35. django_admin_tables-0.1.0/docs/themes/unfold/row-actions.png +0 -0
  36. django_admin_tables-0.1.0/docs/themes/unfold.md +84 -0
  37. django_admin_tables-0.1.0/example/example/__init__.py +0 -0
  38. django_admin_tables-0.1.0/example/example/settings.py +68 -0
  39. django_admin_tables-0.1.0/example/example/settings_unfold.py +8 -0
  40. django_admin_tables-0.1.0/example/example/urls.py +6 -0
  41. django_admin_tables-0.1.0/example/example/urls_unfold.py +6 -0
  42. django_admin_tables-0.1.0/example/example/wsgi.py +7 -0
  43. django_admin_tables-0.1.0/example/gallery/__init__.py +0 -0
  44. django_admin_tables-0.1.0/example/gallery/admin.py +94 -0
  45. django_admin_tables-0.1.0/example/gallery/admin_unfold.py +101 -0
  46. django_admin_tables-0.1.0/example/gallery/migrations/0001_initial.py +46 -0
  47. django_admin_tables-0.1.0/example/gallery/migrations/__init__.py +0 -0
  48. django_admin_tables-0.1.0/example/gallery/models.py +29 -0
  49. django_admin_tables-0.1.0/example/manage.py +20 -0
  50. django_admin_tables-0.1.0/package-lock.json +2963 -0
  51. django_admin_tables-0.1.0/package.json +16 -0
  52. django_admin_tables-0.1.0/pyproject.toml +176 -0
  53. django_admin_tables-0.1.0/src/django_admin_tables/__init__.py +22 -0
  54. django_admin_tables-0.1.0/src/django_admin_tables/apps.py +6 -0
  55. django_admin_tables-0.1.0/src/django_admin_tables/base.py +102 -0
  56. django_admin_tables-0.1.0/src/django_admin_tables/columns/__init__.py +5 -0
  57. django_admin_tables-0.1.0/src/django_admin_tables/columns/mixins.py +206 -0
  58. django_admin_tables-0.1.0/src/django_admin_tables/columns/responsive.py +54 -0
  59. django_admin_tables-0.1.0/src/django_admin_tables/fk_links/__init__.py +5 -0
  60. django_admin_tables-0.1.0/src/django_admin_tables/fk_links/mixins.py +134 -0
  61. django_admin_tables-0.1.0/src/django_admin_tables/footer/__init__.py +6 -0
  62. django_admin_tables-0.1.0/src/django_admin_tables/footer/mixins.py +212 -0
  63. django_admin_tables-0.1.0/src/django_admin_tables/footer/rows.py +194 -0
  64. django_admin_tables-0.1.0/src/django_admin_tables/locale/es/LC_MESSAGES/django.mo +0 -0
  65. django_admin_tables-0.1.0/src/django_admin_tables/locale/es/LC_MESSAGES/django.po +64 -0
  66. django_admin_tables-0.1.0/src/django_admin_tables/mixins.py +38 -0
  67. django_admin_tables-0.1.0/src/django_admin_tables/py.typed +0 -0
  68. django_admin_tables-0.1.0/src/django_admin_tables/radio_select/__init__.py +6 -0
  69. django_admin_tables-0.1.0/src/django_admin_tables/radio_select/formsets.py +55 -0
  70. django_admin_tables-0.1.0/src/django_admin_tables/radio_select/mixins.py +126 -0
  71. django_admin_tables-0.1.0/src/django_admin_tables/radio_select/widgets.py +64 -0
  72. django_admin_tables-0.1.0/src/django_admin_tables/row_actions/__init__.py +6 -0
  73. django_admin_tables-0.1.0/src/django_admin_tables/row_actions/actions.py +250 -0
  74. django_admin_tables-0.1.0/src/django_admin_tables/row_actions/mixins.py +427 -0
  75. django_admin_tables-0.1.0/src/django_admin_tables/static/django_admin_tables/columns.css +138 -0
  76. django_admin_tables-0.1.0/src/django_admin_tables/static/django_admin_tables/columns.js +215 -0
  77. django_admin_tables-0.1.0/src/django_admin_tables/static/django_admin_tables/footer-unfold.css +34 -0
  78. django_admin_tables-0.1.0/src/django_admin_tables/static/django_admin_tables/footer.css +53 -0
  79. django_admin_tables-0.1.0/src/django_admin_tables/static/django_admin_tables/radio-select.js +35 -0
  80. django_admin_tables-0.1.0/src/django_admin_tables/static/django_admin_tables/row-actions-unfold.css +43 -0
  81. django_admin_tables-0.1.0/src/django_admin_tables/static/django_admin_tables/row-actions.css +49 -0
  82. django_admin_tables-0.1.0/src/django_admin_tables/static/django_admin_tables/row-actions.js +156 -0
  83. django_admin_tables-0.1.0/src/django_admin_tables/static/django_admin_tables/unsaved-changes.js +46 -0
  84. django_admin_tables-0.1.0/src/django_admin_tables/templates/django_admin_tables/change_list.html +31 -0
  85. django_admin_tables-0.1.0/src/django_admin_tables/templates/django_admin_tables/columns_menu.html +17 -0
  86. django_admin_tables-0.1.0/src/django_admin_tables/templates/django_admin_tables/edit_inline.html +8 -0
  87. django_admin_tables-0.1.0/src/django_admin_tables/templates/django_admin_tables/footer_summary.html +21 -0
  88. django_admin_tables-0.1.0/src/django_admin_tables/templates/django_admin_tables/row_actions.html +21 -0
  89. django_admin_tables-0.1.0/src/django_admin_tables/templates/django_admin_tables/tfoot.html +35 -0
  90. django_admin_tables-0.1.0/src/django_admin_tables/templatetags/__init__.py +0 -0
  91. django_admin_tables-0.1.0/src/django_admin_tables/templatetags/django_admin_tables.py +117 -0
  92. django_admin_tables-0.1.0/src/django_admin_tables/unsaved.py +22 -0
  93. django_admin_tables-0.1.0/tests/__init__.py +0 -0
  94. django_admin_tables-0.1.0/tests/conftest.py +9 -0
  95. django_admin_tables-0.1.0/tests/core/__init__.py +0 -0
  96. django_admin_tables-0.1.0/tests/core/admin.py +30 -0
  97. django_admin_tables-0.1.0/tests/core/apps.py +6 -0
  98. django_admin_tables-0.1.0/tests/core/migrations/0001_initial.py +52 -0
  99. django_admin_tables-0.1.0/tests/core/migrations/__init__.py +0 -0
  100. django_admin_tables-0.1.0/tests/core/models.py +16 -0
  101. django_admin_tables-0.1.0/tests/e2e/__init__.py +0 -0
  102. django_admin_tables-0.1.0/tests/e2e/test_e2e.py +413 -0
  103. django_admin_tables-0.1.0/tests/js/columns.test.js +429 -0
  104. django_admin_tables-0.1.0/tests/js/radio-select.test.js +171 -0
  105. django_admin_tables-0.1.0/tests/js/row-actions-loading.test.js +33 -0
  106. django_admin_tables-0.1.0/tests/js/row-actions.test.js +321 -0
  107. django_admin_tables-0.1.0/tests/js/unsaved-changes.test.js +93 -0
  108. django_admin_tables-0.1.0/tests/settings.py +18 -0
  109. django_admin_tables-0.1.0/tests/templates/admin/custom_change_list.html +3 -0
  110. django_admin_tables-0.1.0/tests/templates/fk_links/link.html +1 -0
  111. django_admin_tables-0.1.0/tests/test_admin_fk_links.py +201 -0
  112. django_admin_tables-0.1.0/tests/test_admin_integration.py +83 -0
  113. django_admin_tables-0.1.0/tests/test_admin_tables_mixin.py +65 -0
  114. django_admin_tables-0.1.0/tests/test_changelist.py +80 -0
  115. django_admin_tables-0.1.0/tests/test_exclusive_radio_fields_mixin.py +461 -0
  116. django_admin_tables-0.1.0/tests/test_footer_rows.py +461 -0
  117. django_admin_tables-0.1.0/tests/test_formsets.py +191 -0
  118. django_admin_tables-0.1.0/tests/test_hideable_columns.py +275 -0
  119. django_admin_tables-0.1.0/tests/test_i18n.py +129 -0
  120. django_admin_tables-0.1.0/tests/test_package.py +43 -0
  121. django_admin_tables-0.1.0/tests/test_responsive_columns.py +229 -0
  122. django_admin_tables-0.1.0/tests/test_row_actions.py +493 -0
  123. django_admin_tables-0.1.0/tests/test_unsaved_changes.py +41 -0
  124. django_admin_tables-0.1.0/tests/test_widgets.py +106 -0
  125. django_admin_tables-0.1.0/uv.lock +1496 -0
  126. django_admin_tables-0.1.0/vitest.config.js +15 -0
@@ -0,0 +1,27 @@
1
+ name: Compatibility Matrix
2
+
3
+ on:
4
+ push:
5
+ branches: [master, main]
6
+ pull_request:
7
+ branches: [master, main]
8
+
9
+ jobs:
10
+ tox:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Install uv
16
+ uses: astral-sh/setup-uv@v5
17
+ with:
18
+ enable-cache: true
19
+
20
+ - name: Install Python versions
21
+ run: uv python install 3.10 3.12 3.13 3.14
22
+
23
+ - name: Install dependencies
24
+ run: uv sync
25
+
26
+ - name: Run compatibility matrix (tox)
27
+ run: uv run tox run
@@ -0,0 +1,24 @@
1
+ name: JavaScript Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - name: Setup Node.js
15
+ uses: actions/setup-node@v4
16
+ with:
17
+ node-version: "20"
18
+ cache: "npm"
19
+
20
+ - name: Install dependencies
21
+ run: npm ci
22
+
23
+ - name: Run Tests & Coverage
24
+ run: npm run coverage
@@ -0,0 +1,28 @@
1
+ name: Pre-commit
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+
8
+ jobs:
9
+ pre-commit:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - name: Install uv
15
+ uses: astral-sh/setup-uv@v5
16
+ with:
17
+ enable-cache: true
18
+
19
+ # The hooks run on python3.14 (default_language_version), found
20
+ # through the venv's bin on PATH under `uv run`.
21
+ - name: Set up Python
22
+ run: uv python install 3.14
23
+
24
+ - name: Install dependencies
25
+ run: uv sync --python 3.14
26
+
27
+ - name: Run pre-commit
28
+ run: uv run pre-commit run --all-files
@@ -0,0 +1,31 @@
1
+ name: Python and Playwright Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [master, main]
6
+ pull_request:
7
+ branches: [master, main]
8
+
9
+ jobs:
10
+ test-python:
11
+ name: Python Tests
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v5
18
+ with:
19
+ enable-cache: true
20
+
21
+ - name: Set up Python
22
+ run: uv python install
23
+
24
+ - name: Install dependencies
25
+ run: uv sync
26
+
27
+ - name: Install Playwright browsers
28
+ run: uv run playwright install --with-deps chromium
29
+
30
+ - name: Run Pytest
31
+ run: uv run pytest
@@ -0,0 +1,55 @@
1
+ name: Release
2
+
3
+ on:
4
+ release:
5
+ types:
6
+ - published
7
+
8
+ jobs:
9
+ release-build:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Install uv
16
+ uses: astral-sh/setup-uv@v5
17
+ with:
18
+ enable-cache: true
19
+
20
+ - name: Show project version
21
+ run: |
22
+ grep "version" pyproject.toml
23
+
24
+ - name: Build release distributions
25
+ run: uv build
26
+
27
+ - name: Upload distributions
28
+ uses: actions/upload-artifact@v4
29
+ with:
30
+ name: release-dists
31
+ path: dist/
32
+
33
+ pypi-publish:
34
+ runs-on: ubuntu-latest
35
+ needs:
36
+ - release-build
37
+ permissions:
38
+ id-token: write
39
+ contents: read
40
+
41
+ environment:
42
+ name: pypi
43
+ url: https://pypi.org/project/django-admin-tables/
44
+
45
+ steps:
46
+ - name: Retrieve release distributions
47
+ uses: actions/download-artifact@v4
48
+ with:
49
+ name: release-dists
50
+ path: dist/
51
+
52
+ - name: Publish release distributions to PyPI
53
+ uses: pypa/gh-action-pypi-publish@release/v1
54
+ with:
55
+ packages-dir: dist/
@@ -0,0 +1,42 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .installed.cfg
9
+ *.egg
10
+
11
+ # Virtual environments
12
+ .venv/
13
+ venv/
14
+ env/
15
+
16
+ # Testing / coverage
17
+ .pytest_cache/
18
+ .coverage
19
+ .coverage.*
20
+ coverage/
21
+ htmlcov/
22
+ .mypy_cache/
23
+ .ruff_cache/
24
+ .tox/
25
+
26
+ # Django
27
+ *.log
28
+ local_settings.py
29
+ db.sqlite3
30
+ db.sqlite3-journal
31
+ media/
32
+
33
+ # Node / JS
34
+ node_modules/
35
+
36
+ # Editors / OS
37
+ .vscode/
38
+ .idea/
39
+ .DS_Store
40
+
41
+ # Docs
42
+ docs/_build/
@@ -0,0 +1,58 @@
1
+ default_language_version:
2
+ python: &python-version python3.14
3
+
4
+ repos:
5
+ - repo: https://github.com/pre-commit/pre-commit-hooks
6
+ rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
7
+ hooks:
8
+ - id: check-added-large-files
9
+ - id: check-case-conflict
10
+ - id: check-json
11
+ - id: check-merge-conflict
12
+ - id: check-symlinks
13
+ - id: check-toml
14
+ - id: debug-statements
15
+ - id: end-of-file-fixer
16
+ - id: trailing-whitespace
17
+
18
+ - repo: https://github.com/biomejs/pre-commit
19
+ rev: d1ce4972fb5ad09afd33b432bca71e8c5dfbb2b5 # frozen: v2.5.6
20
+ hooks:
21
+ - id: biome-check
22
+ additional_dependencies:
23
+ - '@biomejs/biome@2.3.8'
24
+ language_version: default
25
+
26
+ - repo: https://github.com/adamchainz/django-upgrade
27
+ rev: bdcc9646c249f00ec9781751791e7b75b48b3722 # frozen: 1.31.1
28
+ hooks:
29
+ - id: django-upgrade
30
+ args: [--target-version, "4.2"]
31
+
32
+ - repo: https://github.com/astral-sh/ruff-pre-commit
33
+ rev: 39d9ac5938dadb73df0564a45f163e25ff9fa6e2 # frozen: v0.16.1
34
+ hooks:
35
+ - id: ruff-check
36
+ args: [--fix]
37
+ - id: ruff-format
38
+
39
+ - repo: local
40
+ hooks:
41
+ - id: mypy
42
+ name: mypy
43
+ entry: mypy
44
+ language: python
45
+ additional_dependencies:
46
+ - mypy>=1.10
47
+ - django-stubs[compatible-mypy]>=5
48
+ - django>=4.2
49
+ - pytest-django>=4.8
50
+ types: [python]
51
+ files: ^(src|example)/
52
+ pass_filenames: false
53
+ args: [src/django_admin_tables, example]
54
+
55
+ - repo: https://github.com/tox-dev/pyproject-fmt
56
+ rev: d600c142bb19f521ae6a6a345f94a2efd7937759 # frozen: v2.26.0
57
+ hooks:
58
+ - id: pyproject-fmt
@@ -0,0 +1,56 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-10-17
9
+
10
+ First release.
11
+
12
+ ### Added
13
+
14
+ - `ExclusiveRadioFieldsMixin` (`django_admin_tables.radio_select`):
15
+ `BooleanField` columns shown as radio
16
+ buttons, exclusive across the rows of an inline or of the changelist's
17
+ `list_editable`, checked on the server too.
18
+ - `ForeignKeyLinkMixin` (`django_admin_tables.fk_links`): foreign keys in
19
+ `list_display` as links to their change view, with
20
+ `get_foreign_key_link()`, `foreign_key_link_template` and
21
+ `foreign_key_link_class`.
22
+ - `HideableColumnsMixin` (`django_admin_tables.columns`): a "Columns" menu
23
+ on the changelist to hide or show the columns in `list_display_hideable`
24
+ (`list_display_hidden` start hidden), remembered per user and model in
25
+ the browser. System checks `django_admin_tables.E001` and `E002`.
26
+ - `responsive_columns` (same mixin): columns hidden below a viewport width
27
+ (`"sm"`…`"2xl"` or pixels), with CSS media queries, on the changelist
28
+ and in tabular inlines; disabled in the "Columns" menu while hidden.
29
+ System checks `django_admin_tables.E003` and `E004`.
30
+ - `FooterRowsMixin`: rows of totals, averages… (`footer_rows`) below the
31
+ columns of the changelist and of inlines, as the table's own `<tfoot>`
32
+ (a summary line on stacked inlines). Aggregates run in one query, on
33
+ the filtered rows (`footer_scope = "filtered"`, every page) or the page
34
+ shown (`"page"`). On django-admin-inline-controls' inlines they follow
35
+ its filters, paging and refreshes, without depending on it. System
36
+ checks `django_admin_tables.E005` to
37
+ `E007`.
38
+ - `RowActionsMixin`: buttons on each row (`row_actions`), on the
39
+ changelist (an "Actions" column) and in inlines, with `"view"` and
40
+ `"delete"` built in, `@row_action` for permissions, labels and
41
+ confirmations, and per-row hooks. Each one posts to the `ModelAdmin`
42
+ (`<pk>/row-action/<action>/`) and comes back to the page; on
43
+ django-admin-inline-controls' inlines only the inline is refreshed,
44
+ without depending on it. System checks `django_admin_tables.E008` and
45
+ `E009`.
46
+ - `UnsavedChangesMixin`: a changelist with `list_editable` asks before
47
+ its unsaved edits are lost (pages, filters, sorting, search…), but not
48
+ when it's saved or an action runs (`list_editable_unsaved_warning`).
49
+ - Hideable and responsive columns and row actions work under a strict
50
+ Content Security Policy: no inline `<style>` nor script.
51
+ - `AdminTablesMixin`: every extra in one mixin, on a `ModelAdmin`, a
52
+ `TabularInline` or a `StackedInline`, each off until its option is set.
53
+ - django-unfold support: Unfold's radio and checkbox classes, link colors,
54
+ and the "Columns" menu next to its "Filters" button.
55
+ - Spanish translation.
56
+ - Django 4.2 – 6.1, Python 3.10+.
@@ -0,0 +1,113 @@
1
+ # Contributing
2
+
3
+ ## Development setup
4
+
5
+ With [uv](https://docs.astral.sh/uv/) (recommended):
6
+
7
+ ```bash
8
+ uv sync
9
+ uv run playwright install --with-deps chromium
10
+ npm install
11
+ uv run pre-commit install
12
+ ```
13
+
14
+ Without uv:
15
+
16
+ ```bash
17
+ pip install -e ".[dev]"
18
+ playwright install --with-deps chromium
19
+ npm install
20
+ pre-commit install
21
+ ```
22
+
23
+ ## Running the example project
24
+
25
+ ```bash
26
+ cd example
27
+ python manage.py migrate
28
+ python manage.py createsuperuser
29
+ python manage.py runserver
30
+ ```
31
+
32
+ ## Tests
33
+
34
+ Python (pytest + pytest-django, coverage always on via `addopts`):
35
+
36
+ ```bash
37
+ uv run pytest
38
+ ```
39
+
40
+ `tests/e2e/` (real-browser tests via pytest-playwright, driving a real
41
+ Chromium against `live_server`) runs as part of that same `uv run pytest`
42
+ — no separate command. They need `uv run playwright install chromium`
43
+ once (see Setup above) and are slower than the rest of the suite;
44
+ everything else under `tests/` is unit/integration-level and doesn't
45
+ touch a browser.
46
+
47
+ JavaScript (vitest + jsdom):
48
+
49
+ ```bash
50
+ npm test
51
+ npm run coverage
52
+ ```
53
+
54
+ ## Compatibility matrix (tox)
55
+
56
+ `uv run pytest` above only runs against whatever Django version `uv.lock`
57
+ resolved (the newest one satisfying `dependencies`). To check the full
58
+ supported range — every Django series in `classifiers`, against the
59
+ oldest and newest Python it supports (within this package's own
60
+ `requires-python` floor) — run the tox matrix instead:
61
+
62
+ ```bash
63
+ uv run tox run-parallel # every env, in parallel
64
+ uv run tox -e py314-dj52 # a single env, e.g. to debug one failure
65
+ ```
66
+
67
+ `[tool.tox]` in `pyproject.toml` lists the exact envs. Each one gets its own ephemeral venv (via
68
+ [tox-uv](https://github.com/tox-dev/tox-uv), using uv's own Python
69
+ builds — `uv python install <version>` once for any you don't have yet)
70
+ with only `pytest`/`pytest-django`/`pytest-cov` and that env's pinned
71
+ Django, not the full `dev` group, and skips `tests/e2e/` (no Playwright
72
+ in those envs). This only tests boundaries (oldest + newest Python per
73
+ Django series), not every valid combination — that catches most real
74
+ breakage while staying fast. Runs in CI as a separate
75
+ `compat-matrix.yml` workflow, alongside the regular `pytest.yml`. Update
76
+ the `env_list` here whenever `classifiers` gains or drops a Django
77
+ series.
78
+
79
+ ## Lint / format / type-check
80
+
81
+ ```bash
82
+ uv run ruff check .
83
+ uv run ruff format --check .
84
+ uv run mypy src example
85
+ # or, everything at once:
86
+ uv run pre-commit run --all-files
87
+ ```
88
+
89
+ The mypy pre-commit hook runs against the project venv (it needs
90
+ django-stubs to resolve model types), not an isolated pre-commit env.
91
+
92
+ ## Before opening a PR
93
+
94
+ - [ ] `uv run pytest`
95
+ - [ ] `npm test`
96
+ - [ ] `uv run pre-commit run --all-files`
97
+
98
+ ## Translations
99
+
100
+ Catalogs live in `src/django_admin_tables/locale/`. After changing
101
+ translatable text, from that package directory:
102
+
103
+ ```bash
104
+ cd src/django_admin_tables
105
+ DJANGO_SETTINGS_MODULE=tests.settings PYTHONPATH=../..:../../example \
106
+ uv run django-admin makemessages -l es --no-obsolete --add-location file --no-wrap
107
+ # translate the new/changed entries in locale/es/LC_MESSAGES/django.po, then:
108
+ DJANGO_SETTINGS_MODULE=tests.settings PYTHONPATH=../..:../../example \
109
+ uv run django-admin compilemessages
110
+ ```
111
+
112
+ Commit both the `.po` and the compiled `.mo`. `tests/test_i18n.py` fails if
113
+ an entry is untranslated or fuzzy, or if the `.mo` doesn't match the `.po`.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rodolfo Becerra
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.