PyInventory 0.21.4__tar.gz → 0.22.1__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.
- {pyinventory-0.21.4 → pyinventory-0.22.1}/.gitignore +2 -3
- {pyinventory-0.21.4 → pyinventory-0.22.1}/.pre-commit-config.yaml +1 -1
- {pyinventory-0.21.4 → pyinventory-0.22.1}/.run/Template Django tests.run.xml +1 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/.run/manage.py --help.run.xml +2 -2
- pyinventory-0.22.1/.run/manage.py make_messages.run.xml +25 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/.run/manage.py update_req.run.xml +2 -2
- {pyinventory-0.21.4 → pyinventory-0.22.1}/.run/unittests ___all___.run.xml +1 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/PKG-INFO +11 -4
- {pyinventory-0.21.4 → pyinventory-0.22.1}/README.md +10 -3
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/__init__.py +2 -2
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/admin/item.py +11 -14
- pyinventory-0.22.1/inventory/locale/ca/LC_MESSAGES/django.mo +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/locale/ca/LC_MESSAGES/django.po +19 -1
- pyinventory-0.22.1/inventory/locale/de/LC_MESSAGES/django.mo +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/locale/de/LC_MESSAGES/django.po +21 -3
- pyinventory-0.22.1/inventory/locale/en/LC_MESSAGES/django.mo +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/locale/en/LC_MESSAGES/django.po +21 -3
- pyinventory-0.22.1/inventory/locale/es/LC_MESSAGES/django.mo +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/locale/es/LC_MESSAGES/django.po +19 -1
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/management/commands/seed_data.py +20 -3
- pyinventory-0.22.1/inventory/migrations/0015_itemmaincategory_itemmodel_category.py +32 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/models/item.py +33 -0
- pyinventory-0.22.1/inventory/persistent_filters.py +72 -0
- pyinventory-0.22.1/inventory/tests/test_management_command_seed_data.py +69 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/settings/local.py +0 -3
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/settings/prod.py +14 -2
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_item.py +87 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_item_auto_group_items_1.snapshot.html +10 -2
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_item_normal_user_create_minimal_item_1.snapshot.html +30 -10
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_item_normal_user_create_minimal_item_2.snapshot.html +31 -10
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_superuser_admin_index_1.snapshot.html +17 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_inventory_commands_help_1.snapshot.txt +3 -3
- {pyinventory-0.21.4 → pyinventory-0.22.1}/pyproject.toml +5 -4
- pyinventory-0.22.1/uv.lock +2239 -0
- pyinventory-0.21.4/inventory/locale/ca/LC_MESSAGES/django.mo +0 -0
- pyinventory-0.21.4/inventory/locale/de/LC_MESSAGES/django.mo +0 -0
- pyinventory-0.21.4/inventory/locale/en/LC_MESSAGES/django.mo +0 -0
- pyinventory-0.21.4/inventory/locale/es/LC_MESSAGES/django.mo +0 -0
- pyinventory-0.21.4/inventory/tests/test_management_command_seed_data.py +0 -49
- pyinventory-0.21.4/uv.lock +0 -2124
- {pyinventory-0.21.4 → pyinventory-0.22.1}/.editorconfig +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/.github/workflows/tests.yml +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/.idea/.gitignore +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/.pre-commit-hooks.yaml +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/.run/Template Python.run.xml +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/AUTHORS +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/LICENSE +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/dist/.gitignore +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/admin/__init__.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/admin/base.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/admin/location.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/admin/memo.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/admin/tagulous_fix.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/apps.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/checks.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/ckeditor_upload.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/constants.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/context_processors.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/forms.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/management/__init__.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/management/commands/__init__.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/management/commands/tree.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/middlewares.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0001_initial.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0002_auto_20201017_2211.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0003_auto_20201024_1830.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0004_item_user_images.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0005_serve_uploads_by_django_tools.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0006_refactor_image_model.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0007_add_file_attachment.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0008_last_check_datetime.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0009_add_memo.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0010_version_protect_models.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0011_parent_tree1.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0012_parent_tree2.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0013_alter_itemmodel_location.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0014_alter_itemmodel_description_and_more.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/__init__.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/models/__init__.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/models/base.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/models/links.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/models/location.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/models/memo.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/parent_tree.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/permissions.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/request_dict.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/signals.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/string_utils.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/templates/admin/item/related_items.html +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/templates/admin/location/items.html +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/__init__.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/fixtures/__init__.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/fixtures/users.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/test_admin_location.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/test_admin_location_empty_change_list_1.snapshot.html +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/test_item_images.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/test_link_model.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/test_management_command_tree.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/test_parent_tree.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/test_parent_tree_model.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/__init__.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/__main__.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/middlewares.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/settings/__init__.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/settings/tests.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/templates/admin/base_site.html +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/templates/admin/login.html +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/__init__.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/fixtures.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/mocks.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/playwright_utils.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_item_login_1.snapshot.html +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_memo.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_memo_normal_user_create_minimal_item_1.snapshot.html +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_inventory_commands.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_migrations.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_models_item.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_playwright_admin.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_project_setup.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_readme_history.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/urls.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/wsgi.py +0 -0
- {pyinventory-0.21.4 → pyinventory-0.22.1}/manage.py +0 -0
|
@@ -9,7 +9,6 @@ __pycache__
|
|
|
9
9
|
!.github
|
|
10
10
|
!.run
|
|
11
11
|
!.editorconfig
|
|
12
|
-
!.flake8
|
|
13
12
|
!.gitignore
|
|
14
13
|
!.pre-commit-config.yaml
|
|
15
14
|
!.pre-commit-hooks.yaml
|
|
@@ -20,8 +19,8 @@ __pycache__
|
|
|
20
19
|
!/backups/.gitkeep
|
|
21
20
|
|
|
22
21
|
# from test projects:
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
/static/
|
|
23
|
+
/media/
|
|
25
24
|
*.sqlite3
|
|
26
25
|
*.json
|
|
27
26
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
<option name="PARENT_ENVS" value="true" />
|
|
7
7
|
<envs>
|
|
8
8
|
<env name="PYTHONUNBUFFERED" value="1" />
|
|
9
|
+
<env name="RAISE_SNAPSHOT_ERRORS" value="0" />
|
|
9
10
|
<env name="DJANGO_SETTINGS_MODULE" value="inventory_project.settings.tests" />
|
|
10
11
|
</envs>
|
|
11
12
|
<option name="SDK_HOME" value="" />
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
<option name="SDK_HOME" value="" />
|
|
11
11
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
|
12
12
|
<option name="IS_MODULE_SDK" value="true" />
|
|
13
|
-
<option name="ADD_CONTENT_ROOTS" value="
|
|
14
|
-
<option name="ADD_SOURCE_ROOTS" value="
|
|
13
|
+
<option name="ADD_CONTENT_ROOTS" value="false" />
|
|
14
|
+
<option name="ADD_SOURCE_ROOTS" value="false" />
|
|
15
15
|
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
|
16
16
|
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/manage.py" />
|
|
17
17
|
<option name="PARAMETERS" value="--help" />
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<component name="ProjectRunConfigurationManager">
|
|
2
|
+
<configuration default="false" name="manage.py make_messages" type="PythonConfigurationType" factoryName="Python">
|
|
3
|
+
<module name="PyInventory" />
|
|
4
|
+
<option name="ENV_FILES" value="" />
|
|
5
|
+
<option name="INTERPRETER_OPTIONS" value="" />
|
|
6
|
+
<option name="PARENT_ENVS" value="true" />
|
|
7
|
+
<envs>
|
|
8
|
+
<env name="PYTHONUNBUFFERED" value="1" />
|
|
9
|
+
</envs>
|
|
10
|
+
<option name="SDK_HOME" value="" />
|
|
11
|
+
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
|
12
|
+
<option name="IS_MODULE_SDK" value="true" />
|
|
13
|
+
<option name="ADD_CONTENT_ROOTS" value="false" />
|
|
14
|
+
<option name="ADD_SOURCE_ROOTS" value="false" />
|
|
15
|
+
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
|
16
|
+
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/manage.py" />
|
|
17
|
+
<option name="PARAMETERS" value="make_messages" />
|
|
18
|
+
<option name="SHOW_COMMAND_LINE" value="false" />
|
|
19
|
+
<option name="EMULATE_TERMINAL" value="true" />
|
|
20
|
+
<option name="MODULE_MODE" value="false" />
|
|
21
|
+
<option name="REDIRECT_INPUT" value="false" />
|
|
22
|
+
<option name="INPUT_FILE" value="" />
|
|
23
|
+
<method v="2" />
|
|
24
|
+
</configuration>
|
|
25
|
+
</component>
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
<option name="SDK_HOME" value="" />
|
|
11
11
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
|
12
12
|
<option name="IS_MODULE_SDK" value="true" />
|
|
13
|
-
<option name="ADD_CONTENT_ROOTS" value="
|
|
14
|
-
<option name="ADD_SOURCE_ROOTS" value="
|
|
13
|
+
<option name="ADD_CONTENT_ROOTS" value="false" />
|
|
14
|
+
<option name="ADD_SOURCE_ROOTS" value="false" />
|
|
15
15
|
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
|
16
16
|
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/manage.py" />
|
|
17
17
|
<option name="PARAMETERS" value="update_req" />
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
<option name="PARENT_ENVS" value="true" />
|
|
7
7
|
<envs>
|
|
8
8
|
<env name="PYTHONUNBUFFERED" value="1" />
|
|
9
|
+
<env name="RAISE_SNAPSHOT_ERRORS" value="0" />
|
|
9
10
|
<env name="DJANGO_SETTINGS_MODULE" value="inventory_project.settings.tests" />
|
|
10
11
|
</envs>
|
|
11
12
|
<option name="SDK_HOME" value="" />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyInventory
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.22.1
|
|
4
4
|
Summary: Web based management to catalog things including state and location etc. using Python/Django.
|
|
5
5
|
Project-URL: Documentation, https://github.com/jedie/PyInventory
|
|
6
6
|
Project-URL: Source, https://github.com/jedie/PyInventory
|
|
@@ -196,6 +196,13 @@ To make a new release, do this:
|
|
|
196
196
|
|
|
197
197
|
[comment]: <> (✂✂✂ auto generated history start ✂✂✂)
|
|
198
198
|
|
|
199
|
+
* [v0.22.1](https://github.com/jedie/PyInventory/compare/v0.22.0...v0.22.1)
|
|
200
|
+
* 2025-11-18 - Fix #207 "All" Category can't be selected
|
|
201
|
+
* 2025-11-18 - Update requirements
|
|
202
|
+
* [v0.22.0](https://github.com/jedie/PyInventory/compare/v0.21.4...v0.22.0)
|
|
203
|
+
* 2025-10-16 - Add tests for PersistentRelatedFieldListFilter
|
|
204
|
+
* 2025-09-21 - NEW: Add a persistent "Main Category"
|
|
205
|
+
* 2025-09-21 - fix for django-admin-sortable2/issues/363
|
|
199
206
|
* [v0.21.4](https://github.com/jedie/PyInventory/compare/v0.21.3...v0.21.4)
|
|
200
207
|
* 2025-09-21 - Because of PyPi download errors: release as v0.21.4
|
|
201
208
|
* [v0.21.3](https://github.com/jedie/PyInventory/compare/v0.21.2...v0.21.3)
|
|
@@ -210,15 +217,15 @@ To make a new release, do this:
|
|
|
210
217
|
* 2025-09-20 - pre-commit: remove "default_install_hook_types"
|
|
211
218
|
* 2025-09-20 - Remove obsolete config files
|
|
212
219
|
* 2025-09-20 - Update ReadmeHistoryTestCase
|
|
220
|
+
|
|
221
|
+
<details><summary>Expand older history entries ...</summary>
|
|
222
|
+
|
|
213
223
|
* [v0.21.2](https://github.com/jedie/PyInventory/compare/v0.21.1...v0.21.2)
|
|
214
224
|
* 2025-09-09 - Update project, e.g.: Darker -> Ruff and fix tests
|
|
215
225
|
* 2025-05-01 - Fix local dev server: Don't enforce https
|
|
216
226
|
* [v0.21.1](https://github.com/jedie/PyInventory/compare/v0.21.0...v0.21.1)
|
|
217
227
|
* 2025-05-01 - Replace setuptools with hatchling
|
|
218
228
|
* 2025-04-30 - Update requirements and some small code parts
|
|
219
|
-
|
|
220
|
-
<details><summary>Expand older history entries ...</summary>
|
|
221
|
-
|
|
222
229
|
* [v0.21.0](https://github.com/jedie/PyInventory/compare/v0.20.1...v0.21.0)
|
|
223
230
|
* 2025-03-23 - Bugfix publish: setuptools missing
|
|
224
231
|
* 2025-03-23 - Migrate "pip-tools" -> "uv" and remove tox
|
|
@@ -166,6 +166,13 @@ To make a new release, do this:
|
|
|
166
166
|
|
|
167
167
|
[comment]: <> (✂✂✂ auto generated history start ✂✂✂)
|
|
168
168
|
|
|
169
|
+
* [v0.22.1](https://github.com/jedie/PyInventory/compare/v0.22.0...v0.22.1)
|
|
170
|
+
* 2025-11-18 - Fix #207 "All" Category can't be selected
|
|
171
|
+
* 2025-11-18 - Update requirements
|
|
172
|
+
* [v0.22.0](https://github.com/jedie/PyInventory/compare/v0.21.4...v0.22.0)
|
|
173
|
+
* 2025-10-16 - Add tests for PersistentRelatedFieldListFilter
|
|
174
|
+
* 2025-09-21 - NEW: Add a persistent "Main Category"
|
|
175
|
+
* 2025-09-21 - fix for django-admin-sortable2/issues/363
|
|
169
176
|
* [v0.21.4](https://github.com/jedie/PyInventory/compare/v0.21.3...v0.21.4)
|
|
170
177
|
* 2025-09-21 - Because of PyPi download errors: release as v0.21.4
|
|
171
178
|
* [v0.21.3](https://github.com/jedie/PyInventory/compare/v0.21.2...v0.21.3)
|
|
@@ -180,15 +187,15 @@ To make a new release, do this:
|
|
|
180
187
|
* 2025-09-20 - pre-commit: remove "default_install_hook_types"
|
|
181
188
|
* 2025-09-20 - Remove obsolete config files
|
|
182
189
|
* 2025-09-20 - Update ReadmeHistoryTestCase
|
|
190
|
+
|
|
191
|
+
<details><summary>Expand older history entries ...</summary>
|
|
192
|
+
|
|
183
193
|
* [v0.21.2](https://github.com/jedie/PyInventory/compare/v0.21.1...v0.21.2)
|
|
184
194
|
* 2025-09-09 - Update project, e.g.: Darker -> Ruff and fix tests
|
|
185
195
|
* 2025-05-01 - Fix local dev server: Don't enforce https
|
|
186
196
|
* [v0.21.1](https://github.com/jedie/PyInventory/compare/v0.21.0...v0.21.1)
|
|
187
197
|
* 2025-05-01 - Replace setuptools with hatchling
|
|
188
198
|
* 2025-04-30 - Update requirements and some small code parts
|
|
189
|
-
|
|
190
|
-
<details><summary>Expand older history entries ...</summary>
|
|
191
|
-
|
|
192
199
|
* [v0.21.0](https://github.com/jedie/PyInventory/compare/v0.20.1...v0.21.0)
|
|
193
200
|
* 2025-03-23 - Bugfix publish: setuptools missing
|
|
194
201
|
* 2025-03-23 - Migrate "pip-tools" -> "uv" and remove tox
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
Web based management to catalog things including state and location etc. using Python/Django.
|
|
4
4
|
|
|
5
5
|
created 14.20.2020 by Jens Diemer <opensource@jensdiemer.de>
|
|
6
|
-
:copyleft: 2020-
|
|
6
|
+
:copyleft: 2020-2025 by the PyInventory team, see AUTHORS for more details.
|
|
7
7
|
:license: GNU GPL v3 or above, see LICENSE for more details.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
# See https://packaging.python.org/en/latest/specifications/version-specifiers/
|
|
11
|
-
__version__ = '0.
|
|
11
|
+
__version__ = '0.22.1'
|
|
12
12
|
__author__ = 'Jens Diemer <PyInventory@jensdiemer.de>'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
3
|
import tagulous
|
|
4
|
-
from adminsortable2.admin import SortableAdminMixin, SortableInlineAdminMixin
|
|
4
|
+
from adminsortable2.admin import SortableAdminBase, SortableAdminMixin, SortableInlineAdminMixin
|
|
5
5
|
from django.conf import settings
|
|
6
6
|
from django.contrib import admin
|
|
7
7
|
from django.template.loader import render_to_string
|
|
@@ -20,13 +20,19 @@ from inventory.admin.base import (
|
|
|
20
20
|
)
|
|
21
21
|
from inventory.admin.tagulous_fix import TagulousModelAdminFix
|
|
22
22
|
from inventory.models import ItemLinkModel, ItemModel
|
|
23
|
-
from inventory.models.item import ItemFileModel, ItemImageModel
|
|
23
|
+
from inventory.models.item import ItemFileModel, ItemImageModel, ItemMainCategory
|
|
24
|
+
from inventory.persistent_filters import PersistentRelatedFieldListFilter
|
|
24
25
|
from inventory.string_utils import ltruncatechars
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
logger = logging.getLogger(__name__)
|
|
28
29
|
|
|
29
30
|
|
|
31
|
+
@admin.register(ItemMainCategory)
|
|
32
|
+
class ItemMainCategoryAdmin(SortableAdminMixin, admin.ModelAdmin):
|
|
33
|
+
list_display = ('order', 'name')
|
|
34
|
+
|
|
35
|
+
|
|
30
36
|
class ItemLinkModelInline(UserInlineMixin, SortableInlineAdminMixin, admin.TabularInline):
|
|
31
37
|
model = ItemLinkModel
|
|
32
38
|
extra = 0
|
|
@@ -46,7 +52,7 @@ class ItemModelResource(ModelResource):
|
|
|
46
52
|
|
|
47
53
|
|
|
48
54
|
@admin.register(ItemModel)
|
|
49
|
-
class ItemModelAdmin(TagulousModelAdminFix, ImportExportMixin,
|
|
55
|
+
class ItemModelAdmin(TagulousModelAdminFix, ImportExportMixin, SortableAdminBase, BaseUserAdmin):
|
|
50
56
|
@admin.display(description=_('Related items'))
|
|
51
57
|
def related_items(self, obj):
|
|
52
58
|
if obj.pk is None:
|
|
@@ -95,6 +101,7 @@ class ItemModelAdmin(TagulousModelAdminFix, ImportExportMixin, SortableAdminMixi
|
|
|
95
101
|
ordering = ('path_str',)
|
|
96
102
|
list_display_links = ()
|
|
97
103
|
list_filter = (
|
|
104
|
+
('category', PersistentRelatedFieldListFilter),
|
|
98
105
|
LimitTreeDepthListFilter,
|
|
99
106
|
('kind', admin.RelatedOnlyFieldListFilter),
|
|
100
107
|
('location', admin.RelatedOnlyFieldListFilter),
|
|
@@ -118,7 +125,7 @@ class ItemModelAdmin(TagulousModelAdminFix, ImportExportMixin, SortableAdminMixi
|
|
|
118
125
|
_('Basic'),
|
|
119
126
|
{
|
|
120
127
|
'fields': (
|
|
121
|
-
'kind',
|
|
128
|
+
('category', 'kind'),
|
|
122
129
|
('producer', 'name'),
|
|
123
130
|
'description',
|
|
124
131
|
'tags',
|
|
@@ -161,15 +168,5 @@ class ItemModelAdmin(TagulousModelAdminFix, ImportExportMixin, SortableAdminMixi
|
|
|
161
168
|
readonly_fields = ('id', 'create_dt', 'update_dt', 'user', 'related_items')
|
|
162
169
|
inlines = (ItemImageModelInline, ItemFileModelInline, ItemLinkModelInline)
|
|
163
170
|
|
|
164
|
-
def get_list_display(self, request):
|
|
165
|
-
list_display = list(super().get_list_display(request))
|
|
166
|
-
|
|
167
|
-
# FIXME: SortableAdminMixin.get_list_display() adds this, we didn't need here:
|
|
168
|
-
# See: https://github.com/jrief/django-admin-sortable2/issues/363
|
|
169
|
-
if '_reorder_' in list_display:
|
|
170
|
-
list_display.remove('_reorder_')
|
|
171
|
-
|
|
172
|
-
return list_display
|
|
173
|
-
|
|
174
171
|
|
|
175
172
|
tagulous.admin.enhance(ItemModel, ItemModelAdmin)
|
|
Binary file
|
|
@@ -7,7 +7,7 @@ msgid ""
|
|
|
7
7
|
msgstr ""
|
|
8
8
|
"Project-Id-Version: \n"
|
|
9
9
|
"Report-Msgid-Bugs-To: \n"
|
|
10
|
-
"POT-Creation-Date:
|
|
10
|
+
"POT-Creation-Date: 2025-10-16 10:51+0200\n"
|
|
11
11
|
"PO-Revision-Date: 2022-09-30 20:07+0200\n"
|
|
12
12
|
"Last-Translator: Jaume López\n"
|
|
13
13
|
"Language-Team: \n"
|
|
@@ -102,6 +102,24 @@ msgstr "Nom"
|
|
|
102
102
|
msgid "BaseItemAttachmentModel.name.help_text"
|
|
103
103
|
msgstr ""
|
|
104
104
|
|
|
105
|
+
msgid "ItemMainCategory.name.verbose_name"
|
|
106
|
+
msgstr ""
|
|
107
|
+
|
|
108
|
+
msgid "ItemMainCategory.name.help_text"
|
|
109
|
+
msgstr " "
|
|
110
|
+
|
|
111
|
+
msgid "ItemMainCategory.verbose_name"
|
|
112
|
+
msgstr ""
|
|
113
|
+
|
|
114
|
+
msgid "ItemMainCategory.verbose_name_plural"
|
|
115
|
+
msgstr ""
|
|
116
|
+
|
|
117
|
+
msgid "ItemModel.category.verbose_name"
|
|
118
|
+
msgstr ""
|
|
119
|
+
|
|
120
|
+
msgid "ItemModel.category.help_text"
|
|
121
|
+
msgstr " "
|
|
122
|
+
|
|
105
123
|
msgid "ItemModel.kind.verbose_name"
|
|
106
124
|
msgstr "Tipus"
|
|
107
125
|
|
|
Binary file
|
|
@@ -7,8 +7,8 @@ msgid ""
|
|
|
7
7
|
msgstr ""
|
|
8
8
|
"Project-Id-Version: \n"
|
|
9
9
|
"Report-Msgid-Bugs-To: \n"
|
|
10
|
-
"POT-Creation-Date:
|
|
11
|
-
"PO-Revision-Date:
|
|
10
|
+
"POT-Creation-Date: 2025-10-16 10:51+0200\n"
|
|
11
|
+
"PO-Revision-Date: 2025-09-21 17:34+0200\n"
|
|
12
12
|
"Last-Translator: Jens Diemer\n"
|
|
13
13
|
"Language-Team: \n"
|
|
14
14
|
"Language: de\n"
|
|
@@ -16,7 +16,7 @@ msgstr ""
|
|
|
16
16
|
"Content-Type: text/plain; charset=UTF-8\n"
|
|
17
17
|
"Content-Transfer-Encoding: 8bit\n"
|
|
18
18
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
|
19
|
-
"X-Generator: Poedit 2
|
|
19
|
+
"X-Generator: Poedit 3.4.2\n"
|
|
20
20
|
|
|
21
21
|
msgid "Limit tree depth"
|
|
22
22
|
msgstr "Tiefe des Baumes"
|
|
@@ -101,6 +101,24 @@ msgstr "Name"
|
|
|
101
101
|
msgid "BaseItemAttachmentModel.name.help_text"
|
|
102
102
|
msgstr "Optionalen Namen (Wird automatisch aus dem Dateinamen gesetzt)"
|
|
103
103
|
|
|
104
|
+
msgid "ItemMainCategory.name.verbose_name"
|
|
105
|
+
msgstr "Kategorie"
|
|
106
|
+
|
|
107
|
+
msgid "ItemMainCategory.name.help_text"
|
|
108
|
+
msgstr " "
|
|
109
|
+
|
|
110
|
+
msgid "ItemMainCategory.verbose_name"
|
|
111
|
+
msgstr "Kategorie"
|
|
112
|
+
|
|
113
|
+
msgid "ItemMainCategory.verbose_name_plural"
|
|
114
|
+
msgstr "Kategorien"
|
|
115
|
+
|
|
116
|
+
msgid "ItemModel.category.verbose_name"
|
|
117
|
+
msgstr "Kategorie"
|
|
118
|
+
|
|
119
|
+
msgid "ItemModel.category.help_text"
|
|
120
|
+
msgstr " "
|
|
121
|
+
|
|
104
122
|
msgid "ItemModel.kind.verbose_name"
|
|
105
123
|
msgstr "Art"
|
|
106
124
|
|
|
Binary file
|
|
@@ -7,8 +7,8 @@ msgid ""
|
|
|
7
7
|
msgstr ""
|
|
8
8
|
"Project-Id-Version: \n"
|
|
9
9
|
"Report-Msgid-Bugs-To: \n"
|
|
10
|
-
"POT-Creation-Date:
|
|
11
|
-
"PO-Revision-Date:
|
|
10
|
+
"POT-Creation-Date: 2025-10-16 10:51+0200\n"
|
|
11
|
+
"PO-Revision-Date: 2025-09-21 17:36+0200\n"
|
|
12
12
|
"Last-Translator: Jens Diemer\n"
|
|
13
13
|
"Language-Team: \n"
|
|
14
14
|
"Language: en\n"
|
|
@@ -16,7 +16,7 @@ msgstr ""
|
|
|
16
16
|
"Content-Type: text/plain; charset=UTF-8\n"
|
|
17
17
|
"Content-Transfer-Encoding: 8bit\n"
|
|
18
18
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
|
19
|
-
"X-Generator: Poedit 2
|
|
19
|
+
"X-Generator: Poedit 3.4.2\n"
|
|
20
20
|
|
|
21
21
|
msgid "Limit tree depth"
|
|
22
22
|
msgstr ""
|
|
@@ -101,6 +101,24 @@ msgstr "Name"
|
|
|
101
101
|
msgid "BaseItemAttachmentModel.name.help_text"
|
|
102
102
|
msgstr ""
|
|
103
103
|
|
|
104
|
+
msgid "ItemMainCategory.name.verbose_name"
|
|
105
|
+
msgstr "Name"
|
|
106
|
+
|
|
107
|
+
msgid "ItemMainCategory.name.help_text"
|
|
108
|
+
msgstr " "
|
|
109
|
+
|
|
110
|
+
msgid "ItemMainCategory.verbose_name"
|
|
111
|
+
msgstr "Category"
|
|
112
|
+
|
|
113
|
+
msgid "ItemMainCategory.verbose_name_plural"
|
|
114
|
+
msgstr "Categories"
|
|
115
|
+
|
|
116
|
+
msgid "ItemModel.category.verbose_name"
|
|
117
|
+
msgstr "Category"
|
|
118
|
+
|
|
119
|
+
msgid "ItemModel.category.help_text"
|
|
120
|
+
msgstr " "
|
|
121
|
+
|
|
104
122
|
msgid "ItemModel.kind.verbose_name"
|
|
105
123
|
msgstr "Kind"
|
|
106
124
|
|
|
Binary file
|
|
@@ -7,7 +7,7 @@ msgid ""
|
|
|
7
7
|
msgstr ""
|
|
8
8
|
"Project-Id-Version: \n"
|
|
9
9
|
"Report-Msgid-Bugs-To: \n"
|
|
10
|
-
"POT-Creation-Date:
|
|
10
|
+
"POT-Creation-Date: 2025-10-16 10:51+0200\n"
|
|
11
11
|
"PO-Revision-Date: 2021-10-09 19:36+0200\n"
|
|
12
12
|
"Last-Translator: Jaume López\n"
|
|
13
13
|
"Language-Team: \n"
|
|
@@ -102,6 +102,24 @@ msgstr "Nombre"
|
|
|
102
102
|
msgid "BaseItemAttachmentModel.name.help_text"
|
|
103
103
|
msgstr ""
|
|
104
104
|
|
|
105
|
+
msgid "ItemMainCategory.name.verbose_name"
|
|
106
|
+
msgstr ""
|
|
107
|
+
|
|
108
|
+
msgid "ItemMainCategory.name.help_text"
|
|
109
|
+
msgstr " "
|
|
110
|
+
|
|
111
|
+
msgid "ItemMainCategory.verbose_name"
|
|
112
|
+
msgstr ""
|
|
113
|
+
|
|
114
|
+
msgid "ItemMainCategory.verbose_name_plural"
|
|
115
|
+
msgstr ""
|
|
116
|
+
|
|
117
|
+
msgid "ItemModel.category.verbose_name"
|
|
118
|
+
msgstr ""
|
|
119
|
+
|
|
120
|
+
msgid "ItemModel.category.help_text"
|
|
121
|
+
msgstr " "
|
|
122
|
+
|
|
105
123
|
msgid "ItemModel.kind.verbose_name"
|
|
106
124
|
msgstr "Tipos"
|
|
107
125
|
|
|
@@ -7,6 +7,7 @@ from django.contrib.auth.models import User
|
|
|
7
7
|
from django.core.management.base import BaseCommand
|
|
8
8
|
|
|
9
9
|
from inventory.models import ItemModel, LocationModel
|
|
10
|
+
from inventory.models.item import ItemMainCategory
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
SEED_DATA_USER_PREFIX = 'seed-data-user-'
|
|
@@ -46,7 +47,8 @@ def iter_location_chain(user, location_count):
|
|
|
46
47
|
|
|
47
48
|
|
|
48
49
|
class ItemCreator:
|
|
49
|
-
def __init__(self):
|
|
50
|
+
def __init__(self, categories: list[ItemMainCategory]):
|
|
51
|
+
self.categories = itertools.cycle(categories)
|
|
50
52
|
self.equipment_no = itertools.count(start=1)
|
|
51
53
|
self.item_no = itertools.count(start=1)
|
|
52
54
|
self.part_no = itertools.count(start=1)
|
|
@@ -57,10 +59,13 @@ class ItemCreator:
|
|
|
57
59
|
assert user
|
|
58
60
|
assert location
|
|
59
61
|
while True:
|
|
62
|
+
category = next(self.categories)
|
|
63
|
+
|
|
60
64
|
equipment = ItemModel.objects.create(
|
|
61
65
|
user=user,
|
|
66
|
+
category=category,
|
|
62
67
|
location=location,
|
|
63
|
-
name=f'
|
|
68
|
+
name=f'{category.name} {next(self.equipment_no):03}',
|
|
64
69
|
)
|
|
65
70
|
equipment.full_clean()
|
|
66
71
|
yield equipment
|
|
@@ -68,6 +73,7 @@ class ItemCreator:
|
|
|
68
73
|
while True:
|
|
69
74
|
item = ItemModel.objects.create(
|
|
70
75
|
user=user,
|
|
76
|
+
category=category,
|
|
71
77
|
location=location,
|
|
72
78
|
name=f'Item {next(self.item_no):03}',
|
|
73
79
|
parent=equipment,
|
|
@@ -78,6 +84,7 @@ class ItemCreator:
|
|
|
78
84
|
while True:
|
|
79
85
|
part = ItemModel.objects.create(
|
|
80
86
|
user=user,
|
|
87
|
+
category=category,
|
|
81
88
|
location=location,
|
|
82
89
|
name=f'Part {next(self.part_no):03}',
|
|
83
90
|
parent=item,
|
|
@@ -89,6 +96,14 @@ class ItemCreator:
|
|
|
89
96
|
return
|
|
90
97
|
|
|
91
98
|
|
|
99
|
+
def create_categories() -> list[ItemMainCategory]:
|
|
100
|
+
categories = []
|
|
101
|
+
for category in ('Retrocomputing', 'Photo Equipment', 'Household Goods'):
|
|
102
|
+
instance = ItemMainCategory.objects.get_or_create(name=category)[0]
|
|
103
|
+
categories.append(instance)
|
|
104
|
+
return categories
|
|
105
|
+
|
|
106
|
+
|
|
92
107
|
class Command(BaseCommand):
|
|
93
108
|
help = 'Fill database with example data'
|
|
94
109
|
|
|
@@ -111,13 +126,15 @@ class Command(BaseCommand):
|
|
|
111
126
|
log_level = logging.WARNING
|
|
112
127
|
|
|
113
128
|
with SetupLogger(level=log_level):
|
|
129
|
+
categories = create_categories()
|
|
130
|
+
|
|
114
131
|
existing_users = User.objects.filter(username__startswith=SEED_DATA_USER_PREFIX)
|
|
115
132
|
for user in existing_users:
|
|
116
133
|
self.stdout.write(f'Clean data from user {user}...')
|
|
117
134
|
info = user.delete()
|
|
118
135
|
self.stdout.write(f'done: {info}')
|
|
119
136
|
|
|
120
|
-
item_creator = ItemCreator()
|
|
137
|
+
item_creator = ItemCreator(categories)
|
|
121
138
|
|
|
122
139
|
for user_no in range(1, user_count + 1):
|
|
123
140
|
self.stdout.write('_' * 100)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Generated by Django 5.1.12 on 2025-10-16 08:44
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
('inventory', '0014_alter_itemmodel_description_and_more'),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.CreateModel(
|
|
15
|
+
name='ItemMainCategory',
|
|
16
|
+
fields=[
|
|
17
|
+
('id', models.SmallAutoField(primary_key=True, serialize=False)),
|
|
18
|
+
('name', models.CharField(help_text='ItemMainCategory.name.help_text', max_length=32, unique=True, verbose_name='ItemMainCategory.name.verbose_name')),
|
|
19
|
+
('order', models.SmallIntegerField(default=0)),
|
|
20
|
+
],
|
|
21
|
+
options={
|
|
22
|
+
'verbose_name': 'ItemMainCategory.verbose_name',
|
|
23
|
+
'verbose_name_plural': 'ItemMainCategory.verbose_name_plural',
|
|
24
|
+
'ordering': ('order', 'name'),
|
|
25
|
+
},
|
|
26
|
+
),
|
|
27
|
+
migrations.AddField(
|
|
28
|
+
model_name='itemmodel',
|
|
29
|
+
name='category',
|
|
30
|
+
field=models.ForeignKey(blank=True, help_text='ItemModel.category.help_text', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='items', to='inventory.itemmaincategory', verbose_name='ItemModel.category.verbose_name'),
|
|
31
|
+
),
|
|
32
|
+
]
|
|
@@ -17,6 +17,29 @@ from inventory.models.links import BaseLink
|
|
|
17
17
|
logger = logging.getLogger(__name__)
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
class ItemMainCategory(models.Model):
|
|
21
|
+
id = models.SmallAutoField(primary_key=True)
|
|
22
|
+
name = models.CharField(
|
|
23
|
+
max_length=32,
|
|
24
|
+
unique=True,
|
|
25
|
+
verbose_name=_('ItemMainCategory.name.verbose_name'),
|
|
26
|
+
help_text=_('ItemMainCategory.name.help_text'),
|
|
27
|
+
)
|
|
28
|
+
order = models.SmallIntegerField(
|
|
29
|
+
default=0,
|
|
30
|
+
blank=False,
|
|
31
|
+
null=False,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def __str__(self):
|
|
35
|
+
return self.name
|
|
36
|
+
|
|
37
|
+
class Meta:
|
|
38
|
+
ordering = ('order', 'name')
|
|
39
|
+
verbose_name = _('ItemMainCategory.verbose_name')
|
|
40
|
+
verbose_name_plural = _('ItemMainCategory.verbose_name_plural')
|
|
41
|
+
|
|
42
|
+
|
|
20
43
|
class ItemQuerySet(models.QuerySet):
|
|
21
44
|
def sort(self):
|
|
22
45
|
return self.order_by('kind', 'producer', 'name')
|
|
@@ -29,6 +52,16 @@ class ItemModel(BaseParentTreeModel, VersionProtectBaseModel):
|
|
|
29
52
|
|
|
30
53
|
objects = ItemQuerySet.as_manager()
|
|
31
54
|
|
|
55
|
+
category = models.ForeignKey(
|
|
56
|
+
ItemMainCategory,
|
|
57
|
+
null=True,
|
|
58
|
+
blank=True,
|
|
59
|
+
on_delete=models.PROTECT,
|
|
60
|
+
related_name='items',
|
|
61
|
+
verbose_name=_('ItemModel.category.verbose_name'),
|
|
62
|
+
help_text=_('ItemModel.category.help_text'),
|
|
63
|
+
)
|
|
64
|
+
|
|
32
65
|
kind = tagulous.models.TagField(
|
|
33
66
|
case_sensitive=False,
|
|
34
67
|
force_lowercase=False,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from django.contrib import admin
|
|
4
|
+
from django.core.cache import cache
|
|
5
|
+
from django.db.models.options import Options
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
CURRENT_CATEGORY_CACHE_KEY_PREFIX = 'persistent_parameter'
|
|
12
|
+
CURRENT_CATEGORY_CACHE_TIMEOUT = 60 * 60 * 24 * 14 # 2 weeks
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def persistent_parameter(request, opts: Options, parameter_name: str):
|
|
16
|
+
cache_key = (
|
|
17
|
+
f'{CURRENT_CATEGORY_CACHE_KEY_PREFIX}_{request.user.pk}_{opts.app_label}_{opts.model_name}_{parameter_name}'
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Collect from GET:
|
|
21
|
+
if parameter_name in request.GET:
|
|
22
|
+
current_value = request.GET[parameter_name] or None
|
|
23
|
+
logger.debug('Store %r to %r', current_value, cache_key)
|
|
24
|
+
cache.set(cache_key, current_value, timeout=CURRENT_CATEGORY_CACHE_TIMEOUT)
|
|
25
|
+
return current_value
|
|
26
|
+
|
|
27
|
+
# Try to restore from cache:
|
|
28
|
+
current_value = cache.get(cache_key)
|
|
29
|
+
logger.debug('Restore %r from %r', current_value, cache_key)
|
|
30
|
+
return current_value
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class PersistentRelatedFieldListFilter(admin.RelatedFieldListFilter):
|
|
34
|
+
"""
|
|
35
|
+
Stores the last filter value in the cache and restores it on next visit.
|
|
36
|
+
Note: Will not restore "is null" filter state!
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, field, request, params, model, model_admin, field_path):
|
|
40
|
+
opts: Options = model._meta
|
|
41
|
+
|
|
42
|
+
# Our own parameter to display all choices:
|
|
43
|
+
self.lookup_kwarg_display_all = '%s__all' % field_path
|
|
44
|
+
display_all = bool(params.pop(self.lookup_kwarg_display_all, None))
|
|
45
|
+
|
|
46
|
+
super().__init__(field, request, params, model, model_admin, field_path)
|
|
47
|
+
|
|
48
|
+
if display_all:
|
|
49
|
+
logger.info('Display all choice for %s, ignore persistent filter for %r', opts.model_name, field_path)
|
|
50
|
+
else:
|
|
51
|
+
current_parameter = persistent_parameter(request, opts, parameter_name=self.lookup_kwarg)
|
|
52
|
+
if current_parameter and not self.lookup_val and not self.lookup_val_isnull:
|
|
53
|
+
logger.info('Restore %r filter for %s with %r', field_path, opts.model_name, current_parameter)
|
|
54
|
+
self.lookup_val = [current_parameter]
|
|
55
|
+
self.used_parameters = {self.lookup_kwarg: self.lookup_val}
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def include_empty_choice(self):
|
|
59
|
+
return True
|
|
60
|
+
|
|
61
|
+
def choices(self, changelist):
|
|
62
|
+
choices = iter(super().choices(changelist))
|
|
63
|
+
|
|
64
|
+
# Change the "All" choice to our own "display all" choice:
|
|
65
|
+
all_choice = next(choices)
|
|
66
|
+
all_choice['query_string'] = changelist.get_query_string(
|
|
67
|
+
new_params={self.lookup_kwarg_display_all: True},
|
|
68
|
+
remove=[self.lookup_kwarg, self.lookup_kwarg_isnull],
|
|
69
|
+
)
|
|
70
|
+
yield all_choice
|
|
71
|
+
|
|
72
|
+
yield from choices
|