From f7204f43d013a2d161e13d7d90058bf10ab3ac3a Mon Sep 17 00:00:00 2001 From: Andreas Zweili Date: Sun, 2 Jul 2023 13:11:04 +0200 Subject: [PATCH 01/11] Add mypy --- flake.nix | 3 +- poetry.lock | 140 ++++++++++++++++++++++++++++++++++++++++++++----- pyproject.toml | 18 +++++++ 3 files changed, 146 insertions(+), 15 deletions(-) diff --git a/flake.nix b/flake.nix index 7318324..aaf1bd2 100644 --- a/flake.nix +++ b/flake.nix @@ -51,7 +51,7 @@ pkgs.python310Packages.pip (pkgs.writeScriptBin "dev" "${builtins.readFile ./dev.sh}") ]; - PYTHON_KEYRING_BACKEND="keyring.backends.fail.Keyring"; + PYTHON_KEYRING_BACKEND = "keyring.backends.fail.Keyring"; shellHook = '' export DJANGO_SETTINGS_MODULE=network_inventory.settings.local ''; @@ -68,6 +68,7 @@ checkInputs = [ pkgs.inventoryDevEnv ]; checkPhase = '' mkdir -p $out + mypy src/ flake8 . --count --show-source --statistics ''; }; diff --git a/poetry.lock b/poetry.lock index 5e1167c..7e83fcb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -322,16 +322,56 @@ python-monkey-business = ">=1.0.0" dev = ["Pillow", "black", "dj-database-url", "django-selenosis", "flake8", "pytest", "pytest-cov", "pytest-django", "pytest-xdist", "selenium"] test = ["Pillow", "dj-database-url", "django-selenosis", "pytest", "pytest-cov", "pytest-django", "pytest-xdist", "selenium"] +[[package]] +name = "django-stubs" +version = "4.2.3" +description = "Mypy stubs for Django" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "django-stubs-4.2.3.tar.gz", hash = "sha256:dadab39b46d9ae8f37a8e879c590f39a9e042b565c03fa0c5a8f754b441b1f23"}, + {file = "django_stubs-4.2.3-py3-none-any.whl", hash = "sha256:e30e2e4927ba14bec587ed2c686404b6b8e473cabe9baca445e7d2e1e0d7b14f"}, +] + +[package.dependencies] +django = "*" +django-stubs-ext = ">=4.2.2" +mypy = ">=1.0.0" +tomli = {version = "*", markers = "python_version < \"3.11\""} +types-pytz = "*" +types-PyYAML = "*" +typing-extensions = "*" + +[package.extras] +compatible-mypy = ["mypy (>=1.4.0,<1.5.0)"] + +[[package]] +name = "django-stubs-ext" +version = "4.2.2" +description = "Monkey-patching and extensions for django-stubs" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "django-stubs-ext-4.2.2.tar.gz", hash = "sha256:c69d1cc46f1c4c3b7894b685a5022c29b2a36c7cfb52e23762eaf357ebfc2c98"}, + {file = "django_stubs_ext-4.2.2-py3-none-any.whl", hash = "sha256:fdacc65a14d2d4b97334b58ff178a5853ec8c8c76cec406e417916ad67536ce4"}, +] + +[package.dependencies] +django = "*" +typing-extensions = "*" + [[package]] name = "django-tables2" -version = "2.5.3" +version = "2.6.0" description = "Table/data-grid framework for Django" category = "main" optional = false python-versions = "*" files = [ - {file = "django-tables2-2.5.3.tar.gz", hash = "sha256:f6c1623aac188d29aae9cf6b4de3211c96c525e49890654bec3359c181600eb9"}, - {file = "django_tables2-2.5.3-py2.py3-none-any.whl", hash = "sha256:e336fdf8899a8fab110550a40cad956064bd4054818e0b972c1893b3e2542168"}, + {file = "django-tables2-2.6.0.tar.gz", hash = "sha256:479eed04007cc04bcf764a6fb7a5e3955d94b878ba7f3a4bd4edbd2f7769e08d"}, + {file = "django_tables2-2.6.0-py2.py3-none-any.whl", hash = "sha256:04f23c1181d93716c67085a3c324b449180fd0c5162ef4619acb0b2d9a166133"}, ] [package.dependencies] @@ -561,6 +601,53 @@ Faker = ">=5.4.0,<12.1" [package.extras] tests = ["Django (>=3.0)", "Flask (>=1.0)", "Marshmallow (>=3.9)", "SQLAlchemy (>=1.1.4)", "flask-sqlalchemy (>=2.1)", "mongoengine (>=0.10.1)", "peewee (>=3.7.0)", "pony (>=0.7)", "psycopg2-binary (>=2.8.4)", "pytest"] +[[package]] +name = "mypy" +version = "1.4.1" +description = "Optional static typing for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mypy-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:566e72b0cd6598503e48ea610e0052d1b8168e60a46e0bfd34b3acf2d57f96a8"}, + {file = "mypy-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca637024ca67ab24a7fd6f65d280572c3794665eaf5edcc7e90a866544076878"}, + {file = "mypy-1.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dde1d180cd84f0624c5dcaaa89c89775550a675aff96b5848de78fb11adabcd"}, + {file = "mypy-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8c4d8e89aa7de683e2056a581ce63c46a0c41e31bd2b6d34144e2c80f5ea53dc"}, + {file = "mypy-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:bfdca17c36ae01a21274a3c387a63aa1aafe72bff976522886869ef131b937f1"}, + {file = "mypy-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7549fbf655e5825d787bbc9ecf6028731973f78088fbca3a1f4145c39ef09462"}, + {file = "mypy-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98324ec3ecf12296e6422939e54763faedbfcc502ea4a4c38502082711867258"}, + {file = "mypy-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141dedfdbfe8a04142881ff30ce6e6653c9685b354876b12e4fe6c78598b45e2"}, + {file = "mypy-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8207b7105829eca6f3d774f64a904190bb2231de91b8b186d21ffd98005f14a7"}, + {file = "mypy-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:16f0db5b641ba159eff72cff08edc3875f2b62b2fa2bc24f68c1e7a4e8232d01"}, + {file = "mypy-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:470c969bb3f9a9efcedbadcd19a74ffb34a25f8e6b0e02dae7c0e71f8372f97b"}, + {file = "mypy-1.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5952d2d18b79f7dc25e62e014fe5a23eb1a3d2bc66318df8988a01b1a037c5b"}, + {file = "mypy-1.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:190b6bab0302cec4e9e6767d3eb66085aef2a1cc98fe04936d8a42ed2ba77bb7"}, + {file = "mypy-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9d40652cc4fe33871ad3338581dca3297ff5f2213d0df345bcfbde5162abf0c9"}, + {file = "mypy-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01fd2e9f85622d981fd9063bfaef1aed6e336eaacca00892cd2d82801ab7c042"}, + {file = "mypy-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2460a58faeea905aeb1b9b36f5065f2dc9a9c6e4c992a6499a2360c6c74ceca3"}, + {file = "mypy-1.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2746d69a8196698146a3dbe29104f9eb6a2a4d8a27878d92169a6c0b74435b6"}, + {file = "mypy-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae704dcfaa180ff7c4cfbad23e74321a2b774f92ca77fd94ce1049175a21c97f"}, + {file = "mypy-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:43d24f6437925ce50139a310a64b2ab048cb2d3694c84c71c3f2a1626d8101dc"}, + {file = "mypy-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c482e1246726616088532b5e964e39765b6d1520791348e6c9dc3af25b233828"}, + {file = "mypy-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43b592511672017f5b1a483527fd2684347fdffc041c9ef53428c8dc530f79a3"}, + {file = "mypy-1.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34a9239d5b3502c17f07fd7c0b2ae6b7dd7d7f6af35fbb5072c6208e76295816"}, + {file = "mypy-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5703097c4936bbb9e9bce41478c8d08edd2865e177dc4c52be759f81ee4dd26c"}, + {file = "mypy-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e02d700ec8d9b1859790c0475df4e4092c7bf3272a4fd2c9f33d87fac4427b8f"}, + {file = "mypy-1.4.1-py3-none-any.whl", hash = "sha256:45d32cec14e7b97af848bddd97d85ea4f0db4d5a149ed9676caa4eb2f7402bb4"}, + {file = "mypy-1.4.1.tar.gz", hash = "sha256:9bbcd9ab8ea1f2e1c8031c21445b511442cc45c89951e49bbf852cbb70755b1b"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -900,14 +987,14 @@ test = ["coverage", "pycodestyle", "pyflakes", "pylint", "pytest", "pytest-cov"] [[package]] name = "python-lsp-server" -version = "1.7.3" +version = "1.7.4" description = "Python Language Server for the Language Server Protocol" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "python-lsp-server-1.7.3.tar.gz", hash = "sha256:a31b0525be6ec831c7d2b476b744e5aa5074633e1d1b77ee97f332cde15983ea"}, - {file = "python_lsp_server-1.7.3-py3-none-any.whl", hash = "sha256:f4ae64834da1d4312067a8ee05a76c7652167c3c90d1cef44022366d695c4c0e"}, + {file = "python-lsp-server-1.7.4.tar.gz", hash = "sha256:c84254485a4d9431b24ecefd59741d21c00165611bcf6037bd7d54d0ed06a197"}, + {file = "python_lsp_server-1.7.4-py3-none-any.whl", hash = "sha256:f8053f7aefcb60af4e91f3fab1a093b15ba0c4688ba67e6ab69e7b73e997b2cb"}, ] [package.dependencies] @@ -1022,22 +1109,23 @@ files = [ [[package]] name = "rope" -version = "1.8.0" +version = "1.9.0" description = "a python refactoring library..." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "rope-1.8.0-py3-none-any.whl", hash = "sha256:0767424ed40ce237dcf1c1f5088054fef960e5b19f4a0850783a259a3600d7bd"}, - {file = "rope-1.8.0.tar.gz", hash = "sha256:3de1d1f1cf2412540c6a150067fe25298175e7c2b72455b6ca8395f61678da82"}, + {file = "rope-1.9.0-py3-none-any.whl", hash = "sha256:2ed32d72cd2c4395bb1d569e38fd4f15d6080cfadd61b6e5c565fd39e3f677aa"}, + {file = "rope-1.9.0.tar.gz", hash = "sha256:f48d708132c0e062b411308732ca13933b976486b4b53d1e804f94ed08d69503"}, ] [package.dependencies] pytoolconfig = {version = ">=1.2.2", extras = ["global"]} [package.extras] -dev = ["build (>=0.7.0)", "pip-tools (>=6.12.1)", "pre-commit (>=2.20.0)", "pytest (>=7.0.1)", "pytest-timeout (>=2.1.0)", "toml (>=0.10.2)"] +dev = ["build (>=0.7.0)", "pre-commit (>=2.20.0)", "pytest (>=7.0.1)", "pytest-timeout (>=2.1.0)"] doc = ["pytoolconfig[doc]", "sphinx (>=4.5.0)", "sphinx-autodoc-typehints (>=1.18.1)", "sphinx-rtd-theme (>=1.0.0)"] +release = ["pip-tools (>=6.12.1)", "toml (>=0.10.2)", "twine (>=4.0.2)"] [[package]] name = "setuptools" @@ -1109,16 +1197,40 @@ files = [ {file = "tomlkit-0.11.8.tar.gz", hash = "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"}, ] +[[package]] +name = "types-pytz" +version = "2023.3.0.0" +description = "Typing stubs for pytz" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "types-pytz-2023.3.0.0.tar.gz", hash = "sha256:ecdc70d543aaf3616a7e48631543a884f74205f284cefd6649ddf44c6a820aac"}, + {file = "types_pytz-2023.3.0.0-py3-none-any.whl", hash = "sha256:4fc2a7fbbc315f0b6630e0b899fd6c743705abe1094d007b0e612d10da15e0f3"}, +] + +[[package]] +name = "types-pyyaml" +version = "6.0.12.10" +description = "Typing stubs for PyYAML" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "types-PyYAML-6.0.12.10.tar.gz", hash = "sha256:ebab3d0700b946553724ae6ca636ea932c1b0868701d4af121630e78d695fc97"}, + {file = "types_PyYAML-6.0.12.10-py3-none-any.whl", hash = "sha256:662fa444963eff9b68120d70cda1af5a5f2aa57900003c2006d7626450eaae5f"}, +] + [[package]] name = "typing-extensions" -version = "4.6.3" +version = "4.7.0" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, - {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, + {file = "typing_extensions-4.7.0-py3-none-any.whl", hash = "sha256:5d8c9dac95c27d20df12fb1d97b9793ab8b2af8a3a525e68c80e21060c161771"}, + {file = "typing_extensions-4.7.0.tar.gz", hash = "sha256:935ccf31549830cda708b42289d44b6f74084d616a00be651601a4f968e77c82"}, ] [[package]] @@ -1304,4 +1416,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "95419a1919aadc2d6bbd680b89480fa6b53f4dc29d2f7e2ea7ec7f849bfba1ba" +content-hash = "bb2e80ca320f2ce8368226db9226aa5da713b24cba28a8e655eadb7e2d0b8b11" diff --git a/pyproject.toml b/pyproject.toml index 10eed93..461646b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,22 @@ packages = [ { include = "src" }, ] +[tool.mypy] +exclude = [ + "tests/", +] +[[tool.mypy.overrides]] +module = [ + "nested_admin.*", + "django_tables2.*", + "floppyforms.*", + "django_filters.*", + "crispy_forms.*", + "mixer.*", + "guardian.*", +] +ignore_missing_imports = true + [tool.poetry.group.main.dependencies] python = "^3.9" Django = "^4.1.3" @@ -42,6 +58,8 @@ pytest-xdist = "^3.1.0" rope = "^1.5.1" yapf = "^0.32.0" python-lsp-server = "^1.7.3" +mypy = "^1.4.1" +django-stubs = "^4.2.3" [build-system] requires = ["poetry-core>=1.0.0"] From 6d1611bafa9d2d2d26b31d47372b1b633eb22514 Mon Sep 17 00:00:00 2001 From: Andreas Zweili Date: Sun, 2 Jul 2023 13:42:42 +0200 Subject: [PATCH 02/11] Correct the command to run mypy in flake --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index aaf1bd2..2cdee1f 100644 --- a/flake.nix +++ b/flake.nix @@ -68,7 +68,7 @@ checkInputs = [ pkgs.inventoryDevEnv ]; checkPhase = '' mkdir -p $out - mypy src/ + cd src/ && mypy --config-file=../pyproject.toml . flake8 . --count --show-source --statistics ''; }; From c09184686bab7107639c0da49d519105b5714ed2 Mon Sep 17 00:00:00 2001 From: Andreas Zweili Date: Sun, 2 Jul 2023 13:42:54 +0200 Subject: [PATCH 03/11] Add mypy django plugin --- pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 461646b..3984a1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,11 @@ packages = [ exclude = [ "tests/", ] +plugins = ["mypy_django_plugin.main"] + +[tool.django-stubs] +django_settings_module = "network_inventory.settings.local" + [[tool.mypy.overrides]] module = [ "nested_admin.*", From b3c1f10718e1857eb69e751c8ea9764262078c25 Mon Sep 17 00:00:00 2001 From: Andreas Zweili Date: Sun, 2 Jul 2023 13:43:43 +0200 Subject: [PATCH 04/11] Ignore types for DeleteView This is caused by a bug: https://github.com/typeddjango/django-stubs/issues/1227 --- src/backups/views.py | 4 ++-- src/computers/views.py | 14 +++++++------- src/customers/views.py | 2 +- src/devices/views.py | 6 +++--- src/licenses/views.py | 6 +++--- src/nets/views.py | 2 +- src/users/views.py | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/backups/views.py b/src/backups/views.py index b4fe2d6..4b878de 100644 --- a/src/backups/views.py +++ b/src/backups/views.py @@ -63,7 +63,7 @@ class BackupCreateView(LoginRequiredMixin, CreateView): } -class BackupDeleteView(LoginRequiredMixin, DeleteView): +class BackupDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = Backup template_name = "backups/backup_confirm_delete.html" @@ -71,7 +71,7 @@ class BackupDeleteView(LoginRequiredMixin, DeleteView): return reverse("computer", args=(self.object.computer.pk,)) -class BackupDeleteFromTableView(LoginRequiredMixin, DeleteView): +class BackupDeleteFromTableView(LoginRequiredMixin, DeleteView): # type: ignore model = Backup template_name = "backups/backup_confirm_delete.html" diff --git a/src/computers/views.py b/src/computers/views.py index 36faec7..fc99026 100644 --- a/src/computers/views.py +++ b/src/computers/views.py @@ -152,7 +152,7 @@ def computer_update_view(request, pk): return TemplateResponse(request, template_name, {"form": form}) -class ComputerDeleteView(LoginRequiredMixin, DeleteView): +class ComputerDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = Computer def get_success_url(self): @@ -178,7 +178,7 @@ class ComputerRamRelationCreateView(LoginRequiredMixin, CreateView): } -class ComputerRamRelationDeleteView(LoginRequiredMixin, DeleteView): +class ComputerRamRelationDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = ComputerRamRelation template_name = "computers/relation_confirm_delete.html" @@ -205,7 +205,7 @@ class ComputerCpuRelationCreateView(LoginRequiredMixin, CreateView): } -class ComputerCpuRelationDeleteView(LoginRequiredMixin, DeleteView): +class ComputerCpuRelationDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = ComputerCpuRelation template_name = "computers/relation_confirm_delete.html" @@ -232,7 +232,7 @@ class ComputerGpuRelationCreateView(LoginRequiredMixin, CreateView): } -class ComputerGpuRelationDeleteView(LoginRequiredMixin, DeleteView): +class ComputerGpuRelationDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = ComputerGpuRelation template_name = "computers/relation_confirm_delete.html" @@ -259,7 +259,7 @@ class ComputerDiskRelationCreateView(LoginRequiredMixin, CreateView): } -class ComputerDiskRelationDeleteView(LoginRequiredMixin, DeleteView): +class ComputerDiskRelationDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = ComputerDiskRelation template_name = "computers/relation_confirm_delete.html" @@ -286,7 +286,7 @@ class ComputerSoftwareRelationCreateView(LoginRequiredMixin, CreateView): } -class ComputerSoftwareRelationDeleteView(LoginRequiredMixin, DeleteView): +class ComputerSoftwareRelationDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = ComputerSoftwareRelation template_name = "computers/relation_confirm_delete.html" @@ -313,7 +313,7 @@ class RaidCreateView(LoginRequiredMixin, CreateView): } -class RaidDeleteView(LoginRequiredMixin, DeleteView): +class RaidDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = Raid template_name = "computers/relation_confirm_delete.html" diff --git a/src/customers/views.py b/src/customers/views.py index 38f2dd1..4f7e7c2 100644 --- a/src/customers/views.py +++ b/src/customers/views.py @@ -60,7 +60,7 @@ def customer_detail_view(request, pk): ) -class CustomerDeleteView(LoginRequiredMixin, DeleteView): +class CustomerDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = Customer def get_success_url(self): diff --git a/src/devices/views.py b/src/devices/views.py index e2c95bd..3325be3 100644 --- a/src/devices/views.py +++ b/src/devices/views.py @@ -124,7 +124,7 @@ def device_update_view(request, pk): return TemplateResponse(request, template_name, {"form": form}) -class DeviceDeleteView(LoginRequiredMixin, DeleteView): +class DeviceDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = Device def get_success_url(self): @@ -160,7 +160,7 @@ class WarrantyUpdateView(LoginRequiredMixin, UpdateView): return self.request.POST.get("previous_page") -class WarrantyDeleteView(LoginRequiredMixin, DeleteView): +class WarrantyDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = Warranty def get_success_url(self): @@ -195,7 +195,7 @@ class DeviceInNetUpdateView(LoginRequiredMixin, UpdateView): return self.request.POST.get("previous_page") -class DeviceInNetDeleteView(LoginRequiredMixin, DeleteView): +class DeviceInNetDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = DeviceInNet template_name = "devices/device_in_net_confirm_delete.html" diff --git a/src/licenses/views.py b/src/licenses/views.py index e11ff84..5351db6 100644 --- a/src/licenses/views.py +++ b/src/licenses/views.py @@ -57,7 +57,7 @@ class LicenseWithComputerCreateView(LoginRequiredMixin, CreateView): } -class LicenseWithComputerDeleteView(LoginRequiredMixin, DeleteView): +class LicenseWithComputerDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = LicenseWithComputer template_name = "licenses/license_with_computer_confirm_delete.html" @@ -65,7 +65,7 @@ class LicenseWithComputerDeleteView(LoginRequiredMixin, DeleteView): return reverse("computer", args=(self.object.computer.pk,)) -class UserLicenseDeleteView(LoginRequiredMixin, DeleteView): +class UserLicenseDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = UserLicense template_name = "licenses/license_confirm_delete.html" @@ -73,7 +73,7 @@ class UserLicenseDeleteView(LoginRequiredMixin, DeleteView): return reverse("licenses", args=(self.object.customer.pk,)) -class ComputerLicenseDeleteView(LoginRequiredMixin, DeleteView): +class ComputerLicenseDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = ComputerLicense template_name = "licenses/license_confirm_delete.html" diff --git a/src/nets/views.py b/src/nets/views.py index 040e629..7a2cb7b 100644 --- a/src/nets/views.py +++ b/src/nets/views.py @@ -34,7 +34,7 @@ def net_detail_view(request, pk): ) -class NetDeleteView(LoginRequiredMixin, DeleteView): +class NetDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = Net def get_success_url(self): diff --git a/src/users/views.py b/src/users/views.py index 5029be2..3bd3dd3 100644 --- a/src/users/views.py +++ b/src/users/views.py @@ -52,7 +52,7 @@ def user_detail_view(request, pk): ) -class UserDeleteView(LoginRequiredMixin, DeleteView): +class UserDeleteView(LoginRequiredMixin, DeleteView): # type: ignore model = User def get_success_url(self): From bfb5c19931e2143fe2094a82dd144d2b33689fed Mon Sep 17 00:00:00 2001 From: Andreas Zweili Date: Sun, 2 Jul 2023 13:52:49 +0200 Subject: [PATCH 05/11] Add pylint-django --- poetry.lock | 37 ++++++++++++++++++++++++++++++++++++- pyproject.toml | 6 ++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 7e83fcb..a6defbb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -870,6 +870,41 @@ typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\"" spelling = ["pyenchant (>=3.2,<4.0)"] testutils = ["gitpython (>3)"] +[[package]] +name = "pylint-django" +version = "2.5.3" +description = "A Pylint plugin to help Pylint understand the Django web framework" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "pylint-django-2.5.3.tar.gz", hash = "sha256:0ac090d106c62fe33782a1d01bda1610b761bb1c9bf5035ced9d5f23a13d8591"}, + {file = "pylint_django-2.5.3-py3-none-any.whl", hash = "sha256:56b12b6adf56d548412445bd35483034394a1a94901c3f8571980a13882299d5"}, +] + +[package.dependencies] +pylint = ">=2.0,<3" +pylint-plugin-utils = ">=0.7" + +[package.extras] +for-tests = ["coverage", "django-tables2", "django-tastypie", "factory-boy", "pylint (>=2.13)", "pytest", "wheel"] +with-django = ["Django"] + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +description = "Utilities and helpers for writing Pylint plugins" +category = "dev" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, + {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, +] + +[package.dependencies] +pylint = ">=1.7" + [[package]] name = "pytest" version = "7.4.0" @@ -1416,4 +1451,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "bb2e80ca320f2ce8368226db9226aa5da713b24cba28a8e655eadb7e2d0b8b11" +content-hash = "fc3785e16b3eca7e1a9a434fd263845a51866aec392a7bbe8ee0754c1ee724f9" diff --git a/pyproject.toml b/pyproject.toml index 3984a1e..5500241 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,11 @@ [tool.black] line-length = 79 +[tool.pylint] +load-plugins = [ + "pylint_django", +] + [tool.poetry] name = "network_inventory" version = "0.1.0" @@ -65,6 +70,7 @@ yapf = "^0.32.0" python-lsp-server = "^1.7.3" mypy = "^1.4.1" django-stubs = "^4.2.3" +pylint-django = "^2.5.3" [build-system] requires = ["poetry-core>=1.0.0"] From 729cf08ceaefdc334ce41695747e2090ddc30f11 Mon Sep 17 00:00:00 2001 From: Andreas Zweili Date: Mon, 10 Jul 2023 16:47:01 +0200 Subject: [PATCH 06/11] Use default line length of black --- .flake8 | 8 --- flake.nix | 2 +- pyproject.toml | 22 ++++++- src/backups/decorators.py | 4 +- src/backups/models/backup.py | 16 ++--- src/backups/tests/test_backup_detail_view.py | 8 +-- .../tests/test_customer_backup_table_view.py | 4 +- src/backups/urls.py | 4 +- src/backups/views.py | 2 +- src/computers/forms.py | 12 +--- src/computers/models/computer.py | 8 +-- src/computers/models/raid.py | 4 +- .../tests/test_computer_detail_view.py | 16 ++--- src/computers/tests/test_computer_form.py | 8 +-- .../tests/test_computer_list_view.py | 4 +- .../test_customer_computer_table_view.py | 4 +- src/computers/views.py | 12 +--- src/conftest.py | 4 +- .../test_get_object_with_view_permission.py | 12 +--- .../tests/test_get_objects_for_customer.py | 12 +--- src/core/utils.py | 4 +- src/customers/decorators.py | 4 +- src/customers/tables.py | 12 +--- .../tests/test_customer_list_view.py | 64 +++++-------------- src/customers/tests/test_location_form.py | 4 +- .../tests/test_location_form_view.py | 8 +-- src/customers/views.py | 16 ++--- src/devices/decorators.py | 4 +- src/devices/forms.py | 8 +-- src/devices/models/device.py | 16 ++--- src/devices/models/warranty.py | 4 +- src/devices/tests/test_device_form.py | 12 +--- src/devices/tests/test_device_form_views.py | 8 +-- src/devices/tests/test_warranty_form.py | 10 +-- src/devices/tests/test_warranty_list.py | 8 +-- src/devices/urls.py | 4 +- src/devices/views.py | 12 +--- src/licenses/models.py | 4 +- .../tests/test_customer_license_table_view.py | 4 +- src/licenses/views.py | 2 +- src/manage.py | 4 +- src/nets/decorators.py | 4 +- .../tests/test_views/test_net_detail_view.py | 8 +-- src/nets/views.py | 4 +- src/users/decorators.py | 4 +- .../tests/test_customer_user_table_view.py | 4 +- src/users/tests/test_group_detail_view.py | 4 +- src/users/tests/test_user_detail_view.py | 16 ++--- src/users/views.py | 12 +--- 49 files changed, 122 insertions(+), 312 deletions(-) delete mode 100644 .flake8 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index ea5280d..0000000 --- a/.flake8 +++ /dev/null @@ -1,8 +0,0 @@ -[flake8] -exclude = - *migrations*, - __init__.py, - *cache*, - venv/, - src/manage.py, - src/network_inventory/settings/* diff --git a/flake.nix b/flake.nix index 2cdee1f..c5c268e 100644 --- a/flake.nix +++ b/flake.nix @@ -68,8 +68,8 @@ checkInputs = [ pkgs.inventoryDevEnv ]; checkPhase = '' mkdir -p $out + pylint --errors-only src/ cd src/ && mypy --config-file=../pyproject.toml . - flake8 . --count --show-source --statistics ''; }; tests = pkgs.stdenv.mkDerivation { diff --git a/pyproject.toml b/pyproject.toml index 5500241..3ce9395 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,5 @@ -[tool.black] -line-length = 79 - [tool.pylint] +max-line-length = 88 load-plugins = [ "pylint_django", ] @@ -21,6 +19,24 @@ exclude = [ "tests/", ] plugins = ["mypy_django_plugin.main"] +mypy_path = "./src" +# Start off with these +warn_unused_configs = true +warn_redundant_casts = true +warn_unused_ignores = true + +# Getting these passing should be easy +strict_equality = true +strict_concatenate = true + +# Strongly recommend enabling this one as soon as you can +#check_untyped_defs = true + +# These shouldn't be too much additional work, but may be tricky to +# get passing if you use a lot of untyped libraries +#disallow_subclassing_any = true +#disallow_untyped_decorators = true +#disallow_any_generics = true [tool.django-stubs] django_settings_module = "network_inventory.settings.local" diff --git a/src/backups/decorators.py b/src/backups/decorators.py index abc0ef9..e0bc61c 100644 --- a/src/backups/decorators.py +++ b/src/backups/decorators.py @@ -11,8 +11,6 @@ def backup_view_permission(old_fuction): if user.has_perm("customers.view_customer", backup.computer.customer): return old_fuction(request, pk) else: - return HttpResponseForbidden( - "You're not allowed to access this device." - ) + return HttpResponseForbidden("You're not allowed to access this device.") return new_function diff --git a/src/backups/models/backup.py b/src/backups/models/backup.py index c233c49..88d8a8d 100644 --- a/src/backups/models/backup.py +++ b/src/backups/models/backup.py @@ -20,18 +20,12 @@ class Backup(models.Model): computer = models.ForeignKey( Computer, related_name="source_computer", on_delete=models.CASCADE ) - method = models.ForeignKey( - BackupMethod, models.SET_NULL, blank=True, null=True - ) - software = models.ForeignKey( - Software, models.SET_NULL, blank=True, null=True - ) + method = models.ForeignKey(BackupMethod, models.SET_NULL, blank=True, null=True) + software = models.ForeignKey(Software, models.SET_NULL, blank=True, null=True) source_path = models.CharField(max_length=200, blank=True) exec_time = models.TimeField(null=True, blank=True) exec_days = models.ManyToManyField(Weekday, blank=True) - target_device = models.ManyToManyField( - Computer, through="TargetDevice", blank=True - ) + target_device = models.ManyToManyField(Computer, through="TargetDevice", blank=True) class Meta: ordering = ["name"] @@ -50,9 +44,7 @@ class Backup(models.Model): class TargetDevice(models.Model): - device = models.ForeignKey( - Computer, models.SET_NULL, blank=True, null=True - ) + device = models.ForeignKey(Computer, models.SET_NULL, blank=True, null=True) backup = models.ForeignKey(Backup, on_delete=models.CASCADE) target_path = models.CharField(max_length=200, blank=True) diff --git a/src/backups/tests/test_backup_detail_view.py b/src/backups/tests/test_backup_detail_view.py index 04bd5de..1a01887 100644 --- a/src/backups/tests/test_backup_detail_view.py +++ b/src/backups/tests/test_backup_detail_view.py @@ -53,9 +53,7 @@ def test_backup_detail_view_with_target_device(create_admin_user): software=mixer.SELECT, method=mixer.SELECT, ) - mixer.blend( - "backups.TargetDevice", device=target_computer, backup=mixer.SELECT - ) + mixer.blend("backups.TargetDevice", device=target_computer, backup=mixer.SELECT) client = Client() client.login(username="pharma-admin", password="password") response = client.get("/backup/" + str(backup.id) + "/") @@ -79,9 +77,7 @@ def test_backup_detail_view_with_notification(create_admin_user): client = Client() client.login(username="pharma-admin", password="password") response = client.get("/backup/" + str(backup.id) + "/") - assert response.status_code == 200 and helper.in_content( - response, notification - ) + assert response.status_code == 200 and helper.in_content(response, notification) def test_backup_detail_view_with_day_relation(create_admin_user): diff --git a/src/backups/tests/test_customer_backup_table_view.py b/src/backups/tests/test_customer_backup_table_view.py index 46aa6fb..dea6307 100644 --- a/src/backups/tests/test_customer_backup_table_view.py +++ b/src/backups/tests/test_customer_backup_table_view.py @@ -23,9 +23,7 @@ def test_customer_backup_table(create_admin_user): computer = mixer.blend("computers.Computer", customer=customer) backup = mixer.blend("backups.Backup", computer=computer) response = client.get("/customer/" + str(customer.id) + "/backups/") - assert response.status_code == 200 and helper.in_content( - response, backup.name - ) + assert response.status_code == 200 and helper.in_content(response, backup.name) def test_customer_backup_table_no_backup(create_admin_user): diff --git a/src/backups/urls.py b/src/backups/urls.py index e69c478..f560596 100644 --- a/src/backups/urls.py +++ b/src/backups/urls.py @@ -3,9 +3,7 @@ from django.urls import path from . import views urlpatterns = [ - path( - "customer//backups/", views.backups_table_view, name="backups" - ), + path("customer//backups/", views.backups_table_view, name="backups"), path("backup//", views.backup_detail_view, name="backup"), path( "create/backup-for-computer//", diff --git a/src/backups/views.py b/src/backups/views.py index 4b878de..ac770df 100644 --- a/src/backups/views.py +++ b/src/backups/views.py @@ -47,7 +47,7 @@ def backup_detail_view(request, pk): class BackupCreateView(LoginRequiredMixin, CreateView): model = Backup template_name = "backups/backup_create.html" - fields = "__all__" + fields = "__all__" # type: ignore def get_success_url(self): return reverse("computer", args=(self.computer.pk,)) diff --git a/src/computers/forms.py b/src/computers/forms.py index 0b279ad..ebe2a09 100644 --- a/src/computers/forms.py +++ b/src/computers/forms.py @@ -41,15 +41,9 @@ class ComputerUpdateForm(forms.ModelForm): def __init__(self, request, *args, **kwargs): super(ComputerUpdateForm, self).__init__(*args, **kwargs) - customers = utils.objects_for_allowed_customers( - Customer, user=request.user - ) - locations = utils.objects_for_allowed_customers( - Location, user=request.user - ) - hosts = utils.objects_for_allowed_customers( - Computer, user=request.user - ) + customers = utils.objects_for_allowed_customers(Customer, user=request.user) + locations = utils.objects_for_allowed_customers(Location, user=request.user) + hosts = utils.objects_for_allowed_customers(Computer, user=request.user) users = utils.objects_for_allowed_customers(User, user=request.user) self.fields["customer"].queryset = customers self.fields["location"].queryset = locations diff --git a/src/computers/models/computer.py b/src/computers/models/computer.py index 0b0ce51..bb4a0d2 100644 --- a/src/computers/models/computer.py +++ b/src/computers/models/computer.py @@ -23,12 +23,8 @@ class Computer(Device): ram = models.ManyToManyField(Ram, through="ComputerRamRelation") gpu = models.ManyToManyField(Gpu, through="ComputerGpuRelation") disks = models.ManyToManyField(Disk, through="ComputerDiskRelation") - software = models.ManyToManyField( - Software, through="ComputerSoftwareRelation" - ) - host = models.ForeignKey( - "self", null=True, blank=True, on_delete=models.CASCADE - ) + software = models.ManyToManyField(Software, through="ComputerSoftwareRelation") + host = models.ForeignKey("self", null=True, blank=True, on_delete=models.CASCADE) allocated_space = models.IntegerField(null=True, blank=True) def __str__(self): diff --git a/src/computers/models/raid.py b/src/computers/models/raid.py index a84e3b8..ea2a837 100644 --- a/src/computers/models/raid.py +++ b/src/computers/models/raid.py @@ -12,9 +12,7 @@ class RaidType(Category): class Raid(models.Model): usable_space = models.IntegerField(blank=True, null=True) - raid_type = models.ForeignKey( - RaidType, models.SET_NULL, blank=True, null=True - ) + raid_type = models.ForeignKey(RaidType, models.SET_NULL, blank=True, null=True) computer = models.ForeignKey(Computer, on_delete=models.CASCADE) def __str__(self): diff --git a/src/computers/tests/test_computer_detail_view.py b/src/computers/tests/test_computer_detail_view.py index f698a6d..a80e95e 100644 --- a/src/computers/tests/test_computer_detail_view.py +++ b/src/computers/tests/test_computer_detail_view.py @@ -15,15 +15,11 @@ def test_computer_detail_view_not_logged_in(): def test_computer_detail_view(create_admin_user): create_admin_user() - computer = mixer.blend( - "computers.Computer", customer=mixer.SELECT, os=mixer.SELECT - ) + computer = mixer.blend("computers.Computer", customer=mixer.SELECT, os=mixer.SELECT) client = Client() client.login(username="pharma-admin", password="password") response = client.get("/computer/" + str(computer.id) + "/") - assert response.status_code == 200 and helper.in_content( - response, computer - ) + assert response.status_code == 200 and helper.in_content(response, computer) def test_computer_detail_view_not_found(create_admin_user): @@ -43,9 +39,7 @@ def test_computer_detail_view_ram_relation(create_admin_user): client = Client() client.login(username="pharma-admin", password="password") response = client.get("/computer/" + str(computer.id) + "/") - assert response.status_code == 200 and helper.in_content( - response, "RAM Modules:" - ) + assert response.status_code == 200 and helper.in_content(response, "RAM Modules:") def test_computer_detail_view_raid_relation(create_admin_user): @@ -53,9 +47,7 @@ def test_computer_detail_view_raid_relation(create_admin_user): computer = mixer.blend("computers.Computer", customer=mixer.SELECT) raid_type = mixer.blend("computers.RaidType") disk = mixer.blend("computers.Disk") - raid = mixer.blend( - "computers.Raid", computer=computer, raid_type=raid_type - ) + raid = mixer.blend("computers.Raid", computer=computer, raid_type=raid_type) mixer.blend("computers.DisksInRaid", raid=raid, disk=disk) client = Client() client.login(username="pharma-admin", password="password") diff --git a/src/computers/tests/test_computer_form.py b/src/computers/tests/test_computer_form.py index 765bf1e..4fb6d68 100644 --- a/src/computers/tests/test_computer_form.py +++ b/src/computers/tests/test_computer_form.py @@ -12,9 +12,7 @@ def test_computer_create_form(create_admin_user): fixture = create_admin_user() user = mixer.blend("core.InventoryUser", customer=fixture["customer"]) form = forms.ComputerCreateForm(user=user, data={}) - assert ( - form.is_valid() is False - ), "Should be false because no data was given" + assert form.is_valid() is False, "Should be false because no data was given" data = {"name": "pharma-pc1", "customer": 3} form = forms.ComputerCreateForm(user=user, data=data) @@ -32,9 +30,7 @@ def test_computer_update_form(create_admin_user): request = HttpRequest() request.user = fixture["admin"] form = forms.ComputerUpdateForm(request, data={}) - assert ( - form.is_valid() is False - ), "Should be false because no data was given" + assert form.is_valid() is False, "Should be false because no data was given" data = {"name": "pharma-pc1", "customer": 20356} form = forms.ComputerUpdateForm(request, data=data) diff --git a/src/computers/tests/test_computer_list_view.py b/src/computers/tests/test_computer_list_view.py index 361a0fa..7060cbb 100644 --- a/src/computers/tests/test_computer_list_view.py +++ b/src/computers/tests/test_computer_list_view.py @@ -27,6 +27,4 @@ def test_computer_list_view(create_admin_user): client = Client() client.login(username="pharma-admin", password="password") response = client.get("/computers/all/") - assert response.status_code == 200 and helper.in_content( - response, computer - ) + assert response.status_code == 200 and helper.in_content(response, computer) diff --git a/src/computers/tests/test_customer_computer_table_view.py b/src/computers/tests/test_customer_computer_table_view.py index bc2c039..2ee2954 100644 --- a/src/computers/tests/test_customer_computer_table_view.py +++ b/src/computers/tests/test_customer_computer_table_view.py @@ -21,9 +21,7 @@ def test_customer_computer_table(create_admin_user): client.login(username="pharma-admin", password="password") computer = mixer.blend("computers.Computer", customer=mixer.SELECT) response = client.get("/customer/" + str(customer.id) + "/computers/") - assert response.status_code == 200 and helper.in_content( - response, computer - ) + assert response.status_code == 200 and helper.in_content(response, computer) def test_customer_computer_table_no_computer(create_admin_user): diff --git a/src/computers/views.py b/src/computers/views.py index fc99026..a3c45b4 100644 --- a/src/computers/views.py +++ b/src/computers/views.py @@ -41,9 +41,7 @@ from .tables import ComputersTable @login_required def computer_detail_view(request, pk): - device = utils.get_object_with_view_permission( - Computer, user=request.user, pk=pk - ) + device = utils.get_object_with_view_permission(Computer, user=request.user, pk=pk) disks_relations = ComputerDiskRelation.objects.filter(computer=pk) warranty_relations = Warranty.objects.filter(device=pk) ram_relations = ComputerRamRelation.objects.filter(computer=pk) @@ -76,9 +74,7 @@ def computer_detail_view(request, pk): @login_required def computers_table_view(request, pk): table = ComputersTable( - utils.get_objects_for_customer( - Computer, user=request.user, customer_pk=pk - ) + utils.get_objects_for_customer(Computer, user=request.user, customer_pk=pk) ) RequestConfig(request).configure(table) return render( @@ -139,9 +135,7 @@ def computer_update_view(request, pk): A view to create a customer. """ template_name = "computers/computer_update.html" - computer = utils.get_object_with_view_permission( - Computer, user=request.user, pk=pk - ) + computer = utils.get_object_with_view_permission(Computer, user=request.user, pk=pk) if request.method == "POST": form = ComputerUpdateForm(request, request.POST, instance=computer) if form.is_valid(): diff --git a/src/conftest.py b/src/conftest.py index e5be42e..d5d1b55 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -22,9 +22,7 @@ def django_db_setup(django_db_setup, django_db_blocker): def create_admin_user(): def _create_admin_user(): User = get_user_model() - admin = User.objects.create_user( - "pharma-admin", "admin@pharma.com", "password" - ) + admin = User.objects.create_user("pharma-admin", "admin@pharma.com", "password") customer = mixer.blend("customers.Customer") group = Group.objects.create(name="Pharma Corp. Admin") admin.groups.add(group) diff --git a/src/core/tests/test_get_object_with_view_permission.py b/src/core/tests/test_get_object_with_view_permission.py index da90d74..c0d5336 100644 --- a/src/core/tests/test_get_object_with_view_permission.py +++ b/src/core/tests/test_get_object_with_view_permission.py @@ -15,9 +15,7 @@ def test_get_object_with_view_permission(create_admin_user): fixture = create_admin_user() customer = fixture["customer"] admin = fixture["admin"] - object = utils.get_object_with_view_permission( - Customer, user=admin, pk=customer.id - ) + object = utils.get_object_with_view_permission(Customer, user=admin, pk=customer.id) assert object == customer @@ -26,9 +24,7 @@ def test_get_object_with_view_permission_device(create_admin_user): customer = fixture["customer"] admin = fixture["admin"] device = mixer.blend(Device, customer=customer) - object = utils.get_object_with_view_permission( - Device, user=admin, pk=device.id - ) + object = utils.get_object_with_view_permission(Device, user=admin, pk=device.id) assert object == device @@ -37,9 +33,7 @@ def test_get_object_without_view_permission(create_admin_user): customer = mixer.blend(Customer) admin = fixture["admin"] with pytest.raises(Http404): - utils.get_object_with_view_permission( - Customer, user=admin, pk=customer.id - ) + utils.get_object_with_view_permission(Customer, user=admin, pk=customer.id) def test_get_object_without_view_permission_device(create_admin_user): diff --git a/src/core/tests/test_get_objects_for_customer.py b/src/core/tests/test_get_objects_for_customer.py index ae83108..8990609 100644 --- a/src/core/tests/test_get_objects_for_customer.py +++ b/src/core/tests/test_get_objects_for_customer.py @@ -16,9 +16,7 @@ def test_get_objects_for_customer_with_customer(create_admin_user): customer = fixture["customer"] admin = fixture["admin"] with pytest.raises(Exception): - utils.get_objects_for_customer( - Customer, user=admin, customer_pk=customer.id - ) + utils.get_objects_for_customer(Customer, user=admin, customer_pk=customer.id) def test_get_objects_for_customer_device(create_admin_user): @@ -37,9 +35,7 @@ def test_get_all_objects_for_unallowed_customers(create_admin_user): customer = mixer.blend(Customer) admin = fixture["admin"] with pytest.raises(Http404): - utils.get_objects_for_customer( - Customer, user=admin, customer_pk=customer.id - ) + utils.get_objects_for_customer(Customer, user=admin, customer_pk=customer.id) def test_get_all_objects_for_unallowed_customers_device(create_admin_user): @@ -48,6 +44,4 @@ def test_get_all_objects_for_unallowed_customers_device(create_admin_user): admin = fixture["admin"] mixer.blend(Device, customer=customer) with pytest.raises(Http404): - utils.get_objects_for_customer( - Device, user=admin, customer_pk=customer.id - ) + utils.get_objects_for_customer(Device, user=admin, customer_pk=customer.id) diff --git a/src/core/utils.py b/src/core/utils.py index f3575a4..1483aa9 100644 --- a/src/core/utils.py +++ b/src/core/utils.py @@ -34,9 +34,7 @@ def _get_customers(user): user : django.contrib.auth.models.User """ - return get_objects_for_user( - user, "customers.view_customer", klass=Customer - ) + return get_objects_for_user(user, "customers.view_customer", klass=Customer) def get_object_with_view_permission(model, user=None, pk=None): diff --git a/src/customers/decorators.py b/src/customers/decorators.py index e990333..44bd4f4 100644 --- a/src/customers/decorators.py +++ b/src/customers/decorators.py @@ -10,8 +10,6 @@ def customer_view_permission(old_function): if user.has_perm("customers.view_customer", customer): return old_function(request, pk) else: - return HttpResponseForbidden( - "You're not allowed to access this page." - ) + return HttpResponseForbidden("You're not allowed to access this page.") return new_function diff --git a/src/customers/tables.py b/src/customers/tables.py index 3783880..8b66fa4 100644 --- a/src/customers/tables.py +++ b/src/customers/tables.py @@ -6,9 +6,7 @@ from core.tables import CoreTable class CustomersTable(CoreTable): name = tables.LinkColumn("customer", args=[A("pk")]) - nets = tables.LinkColumn( - "nets", text="Nets", args=[A("pk")], orderable=False - ) + nets = tables.LinkColumn("nets", text="Nets", args=[A("pk")], orderable=False) computers = tables.LinkColumn( "computers", text="Computers", args=[A("pk")], orderable=False ) @@ -21,12 +19,8 @@ class CustomersTable(CoreTable): licenses = tables.LinkColumn( "licenses", text="Licenses", args=[A("pk")], orderable=False ) - users = tables.LinkColumn( - "users", text="Users", args=[A("pk")], orderable=False - ) - groups = tables.LinkColumn( - "groups", text="Groups", args=[A("pk")], orderable=False - ) + users = tables.LinkColumn("users", text="Users", args=[A("pk")], orderable=False) + groups = tables.LinkColumn("groups", text="Groups", args=[A("pk")], orderable=False) project_manager = tables.Column(verbose_name="Project Manager") delete = tables.LinkColumn( "customer_delete", diff --git a/src/customers/tests/test_customer_list_view.py b/src/customers/tests/test_customer_list_view.py index 11be604..bb2bc49 100644 --- a/src/customers/tests/test_customer_list_view.py +++ b/src/customers/tests/test_customer_list_view.py @@ -42,24 +42,12 @@ def test_customer_list_view(create_admin_user): assert ( response.status_code == 200 and helper.in_content(response, customer) - and helper.in_content( - response, "/customer/" + str(customer.id) + "/nets/" - ) - and helper.in_content( - response, "/customer/" + str(customer.id) + "/computers/" - ) - and helper.in_content( - response, "/customer/" + str(customer.id) + "/devices/" - ) - and helper.in_content( - response, "/customer/" + str(customer.id) + "/backups/" - ) - and helper.in_content( - response, "/customer/" + str(customer.id) + "/licenses/" - ) - and helper.in_content( - response, "/customer/" + str(customer.id) + "/users/" - ) + and helper.in_content(response, "/customer/" + str(customer.id) + "/nets/") + and helper.in_content(response, "/customer/" + str(customer.id) + "/computers/") + and helper.in_content(response, "/customer/" + str(customer.id) + "/devices/") + and helper.in_content(response, "/customer/" + str(customer.id) + "/backups/") + and helper.in_content(response, "/customer/" + str(customer.id) + "/licenses/") + and helper.in_content(response, "/customer/" + str(customer.id) + "/users/") and helper.in_content(response, project_manager) ) @@ -75,41 +63,21 @@ def test_customer_list_view_multiple_customers(create_admin_user): assert ( response.status_code == 200 and helper.in_content(response, customer1) - and helper.in_content( - response, "/customer/" + str(customer1.id) + "/nets/" - ) + and helper.in_content(response, "/customer/" + str(customer1.id) + "/nets/") and helper.in_content( response, "/customer/" + str(customer1.id) + "/computers/" ) - and helper.in_content( - response, "/customer/" + str(customer1.id) + "/devices/" - ) - and helper.in_content( - response, "/customer/" + str(customer1.id) + "/backups/" - ) - and helper.in_content( - response, "/customer/" + str(customer1.id) + "/licenses/" - ) - and helper.in_content( - response, "/customer/" + str(customer1.id) + "/users/" - ) + and helper.in_content(response, "/customer/" + str(customer1.id) + "/devices/") + and helper.in_content(response, "/customer/" + str(customer1.id) + "/backups/") + and helper.in_content(response, "/customer/" + str(customer1.id) + "/licenses/") + and helper.in_content(response, "/customer/" + str(customer1.id) + "/users/") and helper.in_content(response, customer2) - and helper.in_content( - response, "/customer/" + str(customer2.id) + "/nets/" - ) + and helper.in_content(response, "/customer/" + str(customer2.id) + "/nets/") and helper.in_content( response, "/customer/" + str(customer2.id) + "/computers/" ) - and helper.in_content( - response, "/customer/" + str(customer2.id) + "/devices/" - ) - and helper.in_content( - response, "/customer/" + str(customer2.id) + "/backups/" - ) - and helper.in_content( - response, "/customer/" + str(customer2.id) + "/licenses/" - ) - and helper.in_content( - response, "/customer/" + str(customer1.id) + "/users/" - ) + and helper.in_content(response, "/customer/" + str(customer2.id) + "/devices/") + and helper.in_content(response, "/customer/" + str(customer2.id) + "/backups/") + and helper.in_content(response, "/customer/" + str(customer2.id) + "/licenses/") + and helper.in_content(response, "/customer/" + str(customer1.id) + "/users/") ) diff --git a/src/customers/tests/test_location_form.py b/src/customers/tests/test_location_form.py index a6cce7e..aba37df 100644 --- a/src/customers/tests/test_location_form.py +++ b/src/customers/tests/test_location_form.py @@ -10,9 +10,7 @@ def test_location_form(create_admin_user): fixture = create_admin_user() user = fixture["admin"] form = forms.LocationForm(user=user, data={}) - assert ( - form.is_valid() is False - ), "Should be false because no data was given" + assert form.is_valid() is False, "Should be false because no data was given" data = {"name": "Main Office", "customer": 3} form = forms.LocationForm(user=user, data=data) diff --git a/src/customers/tests/test_location_form_view.py b/src/customers/tests/test_location_form_view.py index 5483d8b..8bb0dcc 100644 --- a/src/customers/tests/test_location_form_view.py +++ b/src/customers/tests/test_location_form_view.py @@ -14,9 +14,7 @@ def test_load_htmx_create_location_view(create_admin_user): client.login(username="pharma-admin", password="password") url = "/create/location/" response = client.get(url) - assert response.status_code == 200 and helper.in_content( - response, "Add Location" - ) + assert response.status_code == 200 and helper.in_content(response, "Add Location") def test_htmx_create_location_view(create_admin_user): @@ -25,9 +23,7 @@ def test_htmx_create_location_view(create_admin_user): client.login(username="pharma-admin", password="password") data = {"name": mixer.faker.name(), "save_location": 1} response = client.post("/create/location/", data) - assert response.status_code == 200 and helper.in_content( - response, data["name"] - ) + assert response.status_code == 200 and helper.in_content(response, data["name"]) def test_htmx_create_location_view_invalid_form(create_admin_user): diff --git a/src/customers/views.py b/src/customers/views.py index 4f7e7c2..e85cd26 100644 --- a/src/customers/views.py +++ b/src/customers/views.py @@ -24,9 +24,7 @@ def customers_table_view(request): customers = utils.objects_for_allowed_customers(Customer, request.user) table = CustomersTable(customers) RequestConfig(request).configure(table) - return render( - request, "customers/customer_list.html", {"customers": table} - ) + return render(request, "customers/customer_list.html", {"customers": table}) @login_required @@ -44,20 +42,14 @@ def create_customer(request): ) form = CustomerForm() context = {"form": form} - return TemplateResponse( - request, "customers/partials/customer_create.html", context - ) + return TemplateResponse(request, "customers/partials/customer_create.html", context) @login_required def customer_detail_view(request, pk): - customer = utils.get_object_with_view_permission( - Customer, user=request.user, pk=pk - ) + customer = utils.get_object_with_view_permission(Customer, user=request.user, pk=pk) context = {"customer": customer} - return TemplateResponse( - request, "customers/customer_details.html", context - ) + return TemplateResponse(request, "customers/customer_details.html", context) class CustomerDeleteView(LoginRequiredMixin, DeleteView): # type: ignore diff --git a/src/devices/decorators.py b/src/devices/decorators.py index 447386e..8751b5b 100644 --- a/src/devices/decorators.py +++ b/src/devices/decorators.py @@ -11,8 +11,6 @@ def device_view_permission(old_function): if user.has_perm("customers.view_customer", device.customer): return old_function(request, pk) else: - return HttpResponseForbidden( - "You're not allowed to access this device." - ) + return HttpResponseForbidden("You're not allowed to access this device.") return new_function diff --git a/src/devices/forms.py b/src/devices/forms.py index fce0993..fba0e78 100644 --- a/src/devices/forms.py +++ b/src/devices/forms.py @@ -69,12 +69,8 @@ class DeviceUpdateForm(forms.ModelForm): def __init__(self, request, *args, **kwargs): super(DeviceUpdateForm, self).__init__(*args, **kwargs) - customers = utils.objects_for_allowed_customers( - Customer, user=request.user - ) - locations = utils.objects_for_allowed_customers( - Location, user=request.user - ) + customers = utils.objects_for_allowed_customers(Customer, user=request.user) + locations = utils.objects_for_allowed_customers(Location, user=request.user) users = utils.objects_for_allowed_customers(User, user=request.user) self.fields["customer"].queryset = customers self.fields["location"].queryset = locations diff --git a/src/devices/models/device.py b/src/devices/models/device.py index 1f4ebdc..30aff78 100644 --- a/src/devices/models/device.py +++ b/src/devices/models/device.py @@ -38,9 +38,7 @@ class DeviceCategory(Category): class HardwareModel(models.Model): name = models.CharField(max_length=50) - manufacturer = models.ForeignKey( - DeviceManufacturer, on_delete=models.CASCADE - ) + manufacturer = models.ForeignKey(DeviceManufacturer, on_delete=models.CASCADE) class Meta: ordering = ["name"] @@ -56,19 +54,13 @@ class Device(models.Model): category = models.ForeignKey( DeviceCategory, on_delete=models.SET_NULL, null=True, blank=True ) - owner = models.ForeignKey( - Owner, on_delete=models.SET_NULL, null=True, blank=True - ) + owner = models.ForeignKey(Owner, on_delete=models.SET_NULL, null=True, blank=True) customer = models.ForeignKey(Customer, on_delete=models.CASCADE) manufacturer = models.ForeignKey( DeviceManufacturer, models.SET_NULL, null=True, blank=True ) - model = models.ForeignKey( - HardwareModel, models.SET_NULL, null=True, blank=True - ) - location = models.ForeignKey( - Location, models.SET_NULL, null=True, blank=True - ) + model = models.ForeignKey(HardwareModel, models.SET_NULL, null=True, blank=True) + location = models.ForeignKey(Location, models.SET_NULL, null=True, blank=True) user = models.ForeignKey(User, models.SET_NULL, null=True, blank=True) installation_date = models.DateField(null=True, blank=True) net = models.ManyToManyField(Net, through="DeviceInNet") diff --git a/src/devices/models/warranty.py b/src/devices/models/warranty.py index 87b53c4..fb617b5 100644 --- a/src/devices/models/warranty.py +++ b/src/devices/models/warranty.py @@ -16,9 +16,7 @@ class WarrantyType(Category): class Warranty(models.Model): - customer = models.ForeignKey( - Customer, on_delete=models.CASCADE, blank=True - ) + customer = models.ForeignKey(Customer, on_delete=models.CASCADE, blank=True) device = models.ForeignKey(Device, on_delete=models.CASCADE) valid_from = models.DateField() valid_until = models.DateField() diff --git a/src/devices/tests/test_device_form.py b/src/devices/tests/test_device_form.py index d9cc7a0..e3e69d2 100644 --- a/src/devices/tests/test_device_form.py +++ b/src/devices/tests/test_device_form.py @@ -12,9 +12,7 @@ def test_device_create_form(create_admin_user): fixture = create_admin_user() user = mixer.blend("core.InventoryUser", customer=fixture["customer"]) form = forms.DeviceCreateForm(user=user, data={}) - assert ( - form.is_valid() is False - ), "Should be false because no data was given" + assert form.is_valid() is False, "Should be false because no data was given" data = {"name": "pharma-device1", "customer": 3} form = forms.DeviceCreateForm(user=user, data=data) @@ -32,9 +30,7 @@ def test_device_update_form(create_admin_user): request = HttpRequest() request.user = fixture["admin"] form = forms.DeviceUpdateForm(request, data={}) - assert ( - form.is_valid() is False - ), "Should be false because no data was given" + assert form.is_valid() is False, "Should be false because no data was given" data = {"name": "pharma-device1", "customer": 3} form = forms.DeviceUpdateForm(request, data=data) @@ -50,9 +46,7 @@ def test_device_update_form(create_admin_user): def test_device_create_form_duplicate_device(create_admin_user): fixture = create_admin_user() user = mixer.blend("core.InventoryUser", customer=fixture["customer"]) - mixer.blend( - "devices.Device", name="pharma-device1", customer=fixture["customer"] - ) + mixer.blend("devices.Device", name="pharma-device1", customer=fixture["customer"]) data = {"name": "pharma-device1", "customer": fixture["customer"].id} form = forms.DeviceCreateForm(user=user, data=data) assert ( diff --git a/src/devices/tests/test_device_form_views.py b/src/devices/tests/test_device_form_views.py index 775e5bb..8fd5198 100644 --- a/src/devices/tests/test_device_form_views.py +++ b/src/devices/tests/test_device_form_views.py @@ -35,9 +35,7 @@ def test_load_device_update_view(create_admin_user): client.login(username="pharma-admin", password="password") device = mixer.blend("devices.Device", customer=mixer.SELECT) response = client.get("/update/device/{}/".format(device.pk)) - assert response.status_code == 200 and helper.in_content( - response, device.name - ) + assert response.status_code == 200 and helper.in_content(response, device.name) def test_device_update_view(create_admin_user): @@ -187,9 +185,7 @@ def test_device_in_net_update_view(create_admin_user): "mac_address": "", "ip_status": "1", } - response = client.post( - "/update/device-in-net/{}/".format(device_in_net.pk), data - ) + response = client.post("/update/device-in-net/{}/".format(device_in_net.pk), data) assert response.status_code == 302 device_in_net.refresh_from_db() assert device_in_net.ip == data["ip"] diff --git a/src/devices/tests/test_warranty_form.py b/src/devices/tests/test_warranty_form.py index 6fdcde8..620bbc7 100644 --- a/src/devices/tests/test_warranty_form.py +++ b/src/devices/tests/test_warranty_form.py @@ -9,9 +9,7 @@ pytestmark = pytest.mark.django_db def test_warranty_create_form(create_admin_user): create_admin_user() form = forms.WarrantyCreateForm(data={}) - assert ( - form.is_valid() is False - ), "Should be false because no data was given" + assert form.is_valid() is False, "Should be false because no data was given" device = mixer.blend("devices.Device") @@ -33,8 +31,7 @@ def test_warranty_create_form(create_admin_user): form.is_valid() is False ), "Should be false because valid from is before valid until" assert ( - "Valid from date must be before valid until date" - == form.errors["__all__"][0] + "Valid from date must be before valid until date" == form.errors["__all__"][0] ) @@ -51,6 +48,5 @@ def test_warranty_update_form(create_admin_user): form = forms.WarrantyUpdateForm(data=data) assert form.is_valid() is False assert ( - "Valid from date must be before valid until date" - == form.errors["__all__"][0] + "Valid from date must be before valid until date" == form.errors["__all__"][0] ) diff --git a/src/devices/tests/test_warranty_list.py b/src/devices/tests/test_warranty_list.py index 26c0870..d47f94b 100644 --- a/src/devices/tests/test_warranty_list.py +++ b/src/devices/tests/test_warranty_list.py @@ -39,9 +39,7 @@ def test_warranties_view_plenty_of_time(create_admin_user): user.save() device = mixer.blend("devices.Device", customer=fixture["customer"]) more_than_one_year = datetime.date(datetime.today() + timedelta(400)) - mixer.blend( - "devices.Warranty", device=device, valid_until=more_than_one_year - ) + mixer.blend("devices.Warranty", device=device, valid_until=more_than_one_year) client = Client() client.login(username="pharma-admin", password="password") response = client.get("/warranties/") @@ -77,9 +75,7 @@ def test_warranties_view_warranty_one_year_till_expiration(create_admin_user): user.save() device = mixer.blend("devices.Device", customer=fixture["customer"]) not_one_year_more = datetime.date(datetime.today() + timedelta(200)) - mixer.blend( - "devices.Warranty", device=device, valid_until=not_one_year_more - ) + mixer.blend("devices.Warranty", device=device, valid_until=not_one_year_more) client = Client() client.login(username="pharma-admin", password="password") response = client.get("/warranties/") diff --git a/src/devices/urls.py b/src/devices/urls.py index 07f418b..e38b19f 100644 --- a/src/devices/urls.py +++ b/src/devices/urls.py @@ -4,9 +4,7 @@ from . import views urlpatterns = [ - path( - "customer//devices/", views.devices_table_view, name="devices" - ), + path("customer//devices/", views.devices_table_view, name="devices"), path("device//", views.device_detail_view, name="device"), path( "manufacturer//", diff --git a/src/devices/views.py b/src/devices/views.py index 3325be3..4fc8cae 100644 --- a/src/devices/views.py +++ b/src/devices/views.py @@ -60,16 +60,12 @@ def device_detail_view(request, pk): def devices_table_view(request, pk): table = DevicesTable(Device.objects.filter(customer=pk)) RequestConfig(request).configure(table) - return render( - request, "devices/device_list.html", {"devices": table, "pk": pk} - ) + return render(request, "devices/device_list.html", {"devices": table, "pk": pk}) @login_required def warranties_view(request): - table = WarrantiesTable( - utils.objects_for_allowed_customers(Warranty, request.user) - ) + table = WarrantiesTable(utils.objects_for_allowed_customers(Warranty, request.user)) RequestConfig(request).configure(table) return render(request, "devices/warranties_list.html", {"devices": table}) @@ -111,9 +107,7 @@ def device_update_view(request, pk): """ template_name = "devices/device_update.html" request.session["device_to_update"] = pk - device = utils.get_object_with_view_permission( - Device, user=request.user, pk=pk - ) + device = utils.get_object_with_view_permission(Device, user=request.user, pk=pk) if request.method == "POST" and "save_device" in request.POST: form = DeviceUpdateForm(request, request.POST, instance=device) if form.is_valid(): diff --git a/src/licenses/models.py b/src/licenses/models.py index 8eabaa4..044f59c 100644 --- a/src/licenses/models.py +++ b/src/licenses/models.py @@ -48,9 +48,7 @@ class LicenseWithUser(models.Model): class Meta: constraints = [ - models.UniqueConstraint( - fields=["user", "license"], name="user per license" - ) + models.UniqueConstraint(fields=["user", "license"], name="user per license") ] diff --git a/src/licenses/tests/test_customer_license_table_view.py b/src/licenses/tests/test_customer_license_table_view.py index 6be6b8a..931e3ba 100644 --- a/src/licenses/tests/test_customer_license_table_view.py +++ b/src/licenses/tests/test_customer_license_table_view.py @@ -61,9 +61,7 @@ def test_customer_license_table_no_license(create_admin_user): client = Client() client.login(username="pharma-admin", password="password") response = client.get("/customer/" + str(customer.id) + "/licenses/") - assert response.status_code == 200 and helper.not_in_content( - response, customer - ) + assert response.status_code == 200 and helper.not_in_content(response, customer) def test_customer_license_table_no_permission(create_admin_user): diff --git a/src/licenses/views.py b/src/licenses/views.py index 5351db6..8d5ea8d 100644 --- a/src/licenses/views.py +++ b/src/licenses/views.py @@ -41,7 +41,7 @@ def licenses_table_view(request, pk): class LicenseWithComputerCreateView(LoginRequiredMixin, CreateView): model = LicenseWithComputer template_name = "licenses/license_with_computer_create.html" - fields = "__all__" + fields = "__all__" # type: ignore def get_success_url(self): return reverse("computer", args=(self.computer.pk,)) diff --git a/src/manage.py b/src/manage.py index 30bff88..78e5d74 100644 --- a/src/manage.py +++ b/src/manage.py @@ -3,9 +3,7 @@ import os import sys if __name__ == "__main__": - os.environ.setdefault( - "DJANGO_SETTINGS_MODULE", "network_inventory.settings" - ) + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "network_inventory.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: diff --git a/src/nets/decorators.py b/src/nets/decorators.py index 87c1763..fda590f 100644 --- a/src/nets/decorators.py +++ b/src/nets/decorators.py @@ -11,8 +11,6 @@ def net_view_permission(old_fuction): if user.has_perm("customers.view_customer", net.customer): return old_fuction(request, pk) else: - return HttpResponseForbidden( - "You're not allowed to access this device." - ) + return HttpResponseForbidden("You're not allowed to access this device.") return new_function diff --git a/src/nets/tests/test_views/test_net_detail_view.py b/src/nets/tests/test_views/test_net_detail_view.py index 616a0cd..8cda32b 100644 --- a/src/nets/tests/test_views/test_net_detail_view.py +++ b/src/nets/tests/test_views/test_net_detail_view.py @@ -15,9 +15,7 @@ def test_net_detail_view_no_permission(create_admin_user): net = mixer.blend("nets.Net") customer = mixer.blend("customers.Customer") device = mixer.blend("computers.Computer", customer=customer) - mixer.blend( - "devices.DeviceInNet", device=device, net=net, ip="10.7.89.101" - ) + mixer.blend("devices.DeviceInNet", device=device, net=net, ip="10.7.89.101") client = Client() client.login(username="pharma-admin", password="password") response = client.get("/net/" + str(net.id) + "/") @@ -28,9 +26,7 @@ def test_net_detail_view(create_admin_user): fixture = create_admin_user() net = mixer.blend("nets.Net", customer=mixer.SELECT) device = mixer.blend("computers.Computer", customer=fixture["customer"]) - device_in_net = DeviceInNet.objects.create( - device=device, net=net, ip="10.7.89.101" - ) + device_in_net = DeviceInNet.objects.create(device=device, net=net, ip="10.7.89.101") client = Client() client.login(username="pharma-admin", password="password") response = client.get("/net/" + str(net.id) + "/") diff --git a/src/nets/views.py b/src/nets/views.py index 7a2cb7b..7078c5c 100644 --- a/src/nets/views.py +++ b/src/nets/views.py @@ -29,9 +29,7 @@ def net_detail_view(request, pk): net = get_object_or_404(Net, pk=pk) table = NetDetailTable(DeviceInNet.objects.filter(net=net)) RequestConfig(request).configure(table) - return render( - request, "nets/net_details.html", {"table": table, "net": net} - ) + return render(request, "nets/net_details.html", {"table": table, "net": net}) class NetDeleteView(LoginRequiredMixin, DeleteView): # type: ignore diff --git a/src/users/decorators.py b/src/users/decorators.py index 1e0bd47..41e3e3d 100644 --- a/src/users/decorators.py +++ b/src/users/decorators.py @@ -11,8 +11,6 @@ def user_view_permission(old_fuction): if user.has_perm("customers.view_customer", inventory_user.customer): return old_fuction(request, pk) else: - return HttpResponseForbidden( - "You're not allowed to access this device." - ) + return HttpResponseForbidden("You're not allowed to access this device.") return new_function diff --git a/src/users/tests/test_customer_user_table_view.py b/src/users/tests/test_customer_user_table_view.py index dfd994d..825564e 100644 --- a/src/users/tests/test_customer_user_table_view.py +++ b/src/users/tests/test_customer_user_table_view.py @@ -37,9 +37,7 @@ def test_customer_user_table_no_user(create_admin_user): client = Client() client.login(username="pharma-admin", password="password") response = client.get("/customer/" + str(customer.id) + "/users/") - assert response.status_code == 200 and helper.not_in_content( - response, customer - ) + assert response.status_code == 200 and helper.not_in_content(response, customer) def test_customer_user_table_no_permission(create_admin_user): diff --git a/src/users/tests/test_group_detail_view.py b/src/users/tests/test_group_detail_view.py index 5c04f80..aeb476c 100644 --- a/src/users/tests/test_group_detail_view.py +++ b/src/users/tests/test_group_detail_view.py @@ -61,6 +61,4 @@ def test_group_detail_view_with_child_group(create_admin_user): client = Client() client.login(username="pharma-admin", password="password") response = client.get("/group/" + str(group.id) + "/") - assert response.status_code == 200 and helper.in_content( - response, child_group - ) + assert response.status_code == 200 and helper.in_content(response, child_group) diff --git a/src/users/tests/test_user_detail_view.py b/src/users/tests/test_user_detail_view.py index 8043fb9..1ec4a00 100644 --- a/src/users/tests/test_user_detail_view.py +++ b/src/users/tests/test_user_detail_view.py @@ -38,9 +38,7 @@ def test_user_detail_view_group(create_admin_user): client = Client() client.login(username="pharma-admin", password="password") response = client.get("/user/" + str(user.id) + "/") - assert response.status_code == 200 and helper.in_content( - response, "Groups" - ) + assert response.status_code == 200 and helper.in_content(response, "Groups") def test_user_detail_view_mail_alias(create_admin_user): @@ -50,9 +48,7 @@ def test_user_detail_view_mail_alias(create_admin_user): client = Client() client.login(username="pharma-admin", password="password") response = client.get("/user/" + str(user.id) + "/") - assert response.status_code == 200 and helper.in_content( - response, "Mail Alias" - ) + assert response.status_code == 200 and helper.in_content(response, "Mail Alias") def test_user_detail_view_license(create_admin_user): @@ -63,9 +59,7 @@ def test_user_detail_view_license(create_admin_user): client = Client() client.login(username="pharma-admin", password="password") response = client.get("/user/" + str(user.id) + "/") - assert response.status_code == 200 and helper.in_content( - response, "License" - ) + assert response.status_code == 200 and helper.in_content(response, "License") def test_user_detail_view_computer(create_admin_user): @@ -75,9 +69,7 @@ def test_user_detail_view_computer(create_admin_user): client = Client() client.login(username="pharma-admin", password="password") response = client.get("/user/" + str(user.id) + "/") - assert response.status_code == 200 and helper.in_content( - response, computer - ) + assert response.status_code == 200 and helper.in_content(response, computer) def test_user_detail_view_no_permission(create_admin_user): diff --git a/src/users/views.py b/src/users/views.py index 3bd3dd3..05d4ba3 100644 --- a/src/users/views.py +++ b/src/users/views.py @@ -64,9 +64,7 @@ class UserDeleteView(LoginRequiredMixin, DeleteView): # type: ignore def groups_table_view(request, pk): customer = get_object_or_404(Customer, pk=pk) groups_table = GroupsTable( - utils.get_objects_for_customer( - Group, user=request.user, customer_pk=pk - ) + utils.get_objects_for_customer(Group, user=request.user, customer_pk=pk) ) RequestConfig(request).configure(groups_table) return TemplateResponse( @@ -81,9 +79,7 @@ def groups_table_view(request, pk): @login_required def group_detail_view(request, pk): - group = utils.get_object_with_view_permission( - Group, user=request.user, pk=pk - ) + group = utils.get_object_with_view_permission(Group, user=request.user, pk=pk) users = group.user_set.all() groups = Group.objects.filter(parent_group=group) print(groups) @@ -96,9 +92,7 @@ def group_detail_view(request, pk): @login_required def delete_group(request, pk): - group = utils.get_object_with_view_permission( - Group, user=request.user, pk=pk - ) + group = utils.get_object_with_view_permission(Group, user=request.user, pk=pk) if request.method == "POST": group.delete() return redirect("groups", pk=group.customer.pk) From ca30519384de7c99bc288c08963bb4148ae57bce Mon Sep 17 00:00:00 2001 From: Andreas Zweili Date: Mon, 10 Jul 2023 17:36:04 +0200 Subject: [PATCH 07/11] Remove unused dev packages --- poetry.lock | 126 +------------------------------------------------ pyproject.toml | 6 --- 2 files changed, 1 insertion(+), 131 deletions(-) diff --git a/poetry.lock b/poetry.lock index a6defbb..63564a6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -38,22 +38,6 @@ wrapt = [ {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}, ] -[[package]] -name = "autopep8" -version = "2.0.2" -description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "autopep8-2.0.2-py2.py3-none-any.whl", hash = "sha256:86e9303b5e5c8160872b2f5ef611161b2893e9bfe8ccc7e2f76385947d57a2f1"}, - {file = "autopep8-2.0.2.tar.gz", hash = "sha256:f9849cdd62108cb739dbcdbfb7fdcc9a30d1b63c4cc3e1c1f893b5360941b61c"}, -] - -[package.dependencies] -pycodestyle = ">=2.10.0" -tomli = {version = "*", markers = "python_version < \"3.11\""} - [[package]] name = "black" version = "22.12.0" @@ -437,23 +421,6 @@ files = [ [package.dependencies] python-dateutil = ">=2.4" -[[package]] -name = "flake8" -version = "6.0.0" -description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" -optional = false -python-versions = ">=3.8.1" -files = [ - {file = "flake8-6.0.0-py2.py3-none-any.whl", hash = "sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7"}, - {file = "flake8-6.0.0.tar.gz", hash = "sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.10.0,<2.11.0" -pyflakes = ">=3.0.0,<3.1.0" - [[package]] name = "gunicorn" version = "20.1.0" @@ -700,18 +667,6 @@ files = [ {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, ] -[[package]] -name = "pep8" -version = "1.7.1" -description = "Python style guide checker" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "pep8-1.7.1-py2.py3-none-any.whl", hash = "sha256:b22cfae5db09833bb9bd7c8463b53e1a9c9b39f12e304a8d0bba729c501827ee"}, - {file = "pep8-1.7.1.tar.gz", hash = "sha256:fe249b52e20498e59e0b5c5256aa52ee99fc295b26ec9eaa85776ffdb9fe6374"}, -] - [[package]] name = "platformdirs" version = "3.8.0" @@ -816,30 +771,6 @@ files = [ {file = "psycopg2_binary-2.9.6-cp39-cp39-win_amd64.whl", hash = "sha256:f6a88f384335bb27812293fdb11ac6aee2ca3f51d3c7820fe03de0a304ab6249"}, ] -[[package]] -name = "pycodestyle" -version = "2.10.0" -description = "Python style guide checker" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pycodestyle-2.10.0-py2.py3-none-any.whl", hash = "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"}, - {file = "pycodestyle-2.10.0.tar.gz", hash = "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053"}, -] - -[[package]] -name = "pyflakes" -version = "3.0.1" -description = "passive checker of Python programs" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pyflakes-3.0.1-py2.py3-none-any.whl", hash = "sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf"}, - {file = "pyflakes-3.0.1.tar.gz", hash = "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"}, -] - [[package]] name = "pylint" version = "2.17.4" @@ -1069,29 +1000,6 @@ files = [ [package.dependencies] six = ">=1.7.0" -[[package]] -name = "pytoolconfig" -version = "1.2.5" -description = "Python tool configuration" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytoolconfig-1.2.5-py3-none-any.whl", hash = "sha256:239ba9d3e537b91d0243275a497700ea39a5e259ddb80421c366e3b288bf30fe"}, - {file = "pytoolconfig-1.2.5.tar.gz", hash = "sha256:a50f9dfe23b03a9d40414c1fdf902fefbeae12f2ac75a3c8f915944d6ffac279"}, -] - -[package.dependencies] -packaging = ">=22.0" -platformdirs = {version = ">=1.4.4", optional = true, markers = "extra == \"global\""} -tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -doc = ["sphinx (>=4.5.0)", "tabulate (>=0.8.9)"] -gendocs = ["pytoolconfig[doc]", "sphinx (>=4.5.0)", "sphinx-autodoc-typehints (>=1.18.1)", "sphinx-rtd-theme (>=1.0.0)"] -global = ["platformdirs (>=1.4.4)"] -validation = ["pydantic (>=1.7.4)"] - [[package]] name = "pyyaml" version = "6.0" @@ -1142,26 +1050,6 @@ files = [ {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] -[[package]] -name = "rope" -version = "1.9.0" -description = "a python refactoring library..." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "rope-1.9.0-py3-none-any.whl", hash = "sha256:2ed32d72cd2c4395bb1d569e38fd4f15d6080cfadd61b6e5c565fd39e3f677aa"}, - {file = "rope-1.9.0.tar.gz", hash = "sha256:f48d708132c0e062b411308732ca13933b976486b4b53d1e804f94ed08d69503"}, -] - -[package.dependencies] -pytoolconfig = {version = ">=1.2.2", extras = ["global"]} - -[package.extras] -dev = ["build (>=0.7.0)", "pre-commit (>=2.20.0)", "pytest (>=7.0.1)", "pytest-timeout (>=2.1.0)"] -doc = ["pytoolconfig[doc]", "sphinx (>=4.5.0)", "sphinx-autodoc-typehints (>=1.18.1)", "sphinx-rtd-theme (>=1.0.0)"] -release = ["pip-tools (>=6.12.1)", "toml (>=0.10.2)", "twine (>=4.0.2)"] - [[package]] name = "setuptools" version = "68.0.0" @@ -1436,19 +1324,7 @@ files = [ {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, ] -[[package]] -name = "yapf" -version = "0.32.0" -description = "A formatter for Python code." -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "yapf-0.32.0-py2.py3-none-any.whl", hash = "sha256:8fea849025584e486fd06d6ba2bed717f396080fd3cc236ba10cb97c4c51cf32"}, - {file = "yapf-0.32.0.tar.gz", hash = "sha256:a3f5085d37ef7e3e004c4ba9f9b3e40c54ff1901cd111f05145ae313a7c67d1b"}, -] - [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "fc3785e16b3eca7e1a9a434fd263845a51866aec392a7bbe8ee0754c1ee724f9" +content-hash = "56454bd1da936edf92442e1f87d0cdb1ace8cda9297e33f4eba8a98230cc4610" diff --git a/pyproject.toml b/pyproject.toml index 3ce9395..efdf685 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,20 +69,14 @@ psycopg2-binary = "^2.9.5" PyYAML = "^6.0" [tool.poetry.group.dev.dependencies] -autopep8 = "^2.0.0" black = "^22.10.0" coverage = "^6.5.0" -flake8 = "^6.0.0" -jedi = "^0.18.2" mixer = "^7.2.2" -pep8 = "^1.7.1" pylint = "^2.15.8" pytest = "^7.2.0" pytest-cov = "^4.0.0" pytest-django = "^4.5.2" pytest-xdist = "^3.1.0" -rope = "^1.5.1" -yapf = "^0.32.0" python-lsp-server = "^1.7.3" mypy = "^1.4.1" django-stubs = "^4.2.3" From f6cb0f68738bc04c228b6860fd39a0bf65c3d87c Mon Sep 17 00:00:00 2001 From: Andreas Zweili Date: Mon, 10 Jul 2023 17:38:04 +0200 Subject: [PATCH 08/11] Update dependencies --- poetry.lock | 44 ++++++++++++++++++++++---------------------- pyproject.toml | 1 + 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/poetry.lock b/poetry.lock index 63564a6..b1a6c4c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -20,14 +20,14 @@ tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] [[package]] name = "astroid" -version = "2.15.5" +version = "2.15.6" description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false python-versions = ">=3.7.2" files = [ - {file = "astroid-2.15.5-py3-none-any.whl", hash = "sha256:078e5212f9885fa85fbb0cf0101978a336190aadea6e13305409d099f71b2324"}, - {file = "astroid-2.15.5.tar.gz", hash = "sha256:1039262575027b441137ab4a62a793a9b43defb42c32d5670f38686207cd780f"}, + {file = "astroid-2.15.6-py3-none-any.whl", hash = "sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c"}, + {file = "astroid-2.15.6.tar.gz", hash = "sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd"}, ] [package.dependencies] @@ -76,14 +76,14 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "click" -version = "8.1.3" +version = "8.1.4" description = "Composable command line interface toolkit" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, + {file = "click-8.1.4-py3-none-any.whl", hash = "sha256:2739815aaa5d2c986a88f1e9230c55e17f0caad3d958a5e13ad0797c166db9e3"}, + {file = "click-8.1.4.tar.gz", hash = "sha256:b97d0c74955da062a7d4ef92fadb583806a585b2ea81958a81bd72726cbb8e37"}, ] [package.dependencies] @@ -184,14 +184,14 @@ graph = ["objgraph (>=1.7.2)"] [[package]] name = "django" -version = "4.2.2" +version = "4.2.3" description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "Django-4.2.2-py3-none-any.whl", hash = "sha256:672b3fa81e1f853bb58be1b51754108ab4ffa12a77c06db86aa8df9ed0c46fe5"}, - {file = "Django-4.2.2.tar.gz", hash = "sha256:2a6b6fbff5b59dd07bef10bcb019bee2ea97a30b2a656d51346596724324badf"}, + {file = "Django-4.2.3-py3-none-any.whl", hash = "sha256:f7c7852a5ac5a3da5a8d5b35cc6168f31b605971441798dac845f17ca8028039"}, + {file = "Django-4.2.3.tar.gz", hash = "sha256:45a747e1c5b3d6df1b141b1481e193b033fd1fdbda3ff52677dc81afdaacbaed"}, ] [package.dependencies] @@ -259,14 +259,14 @@ Django = ">=2.2" [[package]] name = "django-htmx" -version = "1.15.0" +version = "1.16.0" description = "Extensions for using Django with htmx." category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "django_htmx-1.15.0-py3-none-any.whl", hash = "sha256:b76068485905ad9f911419746f07f877633bb036c0870a0f9fc13dec42c6e425"}, - {file = "django_htmx-1.15.0.tar.gz", hash = "sha256:bac7ff59bf507db6d40424f635f83889ce028a54f47663f227083413de22cabf"}, + {file = "django_htmx-1.16.0-py3-none-any.whl", hash = "sha256:360d11266666e5d92bda57069f671f2c04642eb1fc071e4c4160cf9f504cbfa6"}, + {file = "django_htmx-1.16.0.tar.gz", hash = "sha256:56a65045e079503877ba1354acb481030cf54da1e1f731d33c280e0a14b214cf"}, ] [package.dependencies] @@ -378,14 +378,14 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.1.1" +version = "1.1.2" description = "Backport of PEP 654 (exception groups)" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, - {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, + {file = "exceptiongroup-1.1.2-py3-none-any.whl", hash = "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"}, + {file = "exceptiongroup-1.1.2.tar.gz", hash = "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5"}, ] [package.extras] @@ -669,14 +669,14 @@ files = [ [[package]] name = "platformdirs" -version = "3.8.0" +version = "3.8.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.8.0-py3-none-any.whl", hash = "sha256:ca9ed98ce73076ba72e092b23d3c93ea6c4e186b3f1c3dad6edd98ff6ffcca2e"}, - {file = "platformdirs-3.8.0.tar.gz", hash = "sha256:b0cabcb11063d21a0b261d557acb0a9d2126350e63b70cdf7db6347baea456dc"}, + {file = "platformdirs-3.8.1-py3-none-any.whl", hash = "sha256:cec7b889196b9144d088e4c57d9ceef7374f6c39694ad1577a0aab50d27ea28c"}, + {file = "platformdirs-3.8.1.tar.gz", hash = "sha256:f87ca4fcff7d2b0f81c6a748a77973d7af0f4d526f98f308477c3c436c74d528"}, ] [package.extras] @@ -1146,14 +1146,14 @@ files = [ [[package]] name = "typing-extensions" -version = "4.7.0" +version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.7.0-py3-none-any.whl", hash = "sha256:5d8c9dac95c27d20df12fb1d97b9793ab8b2af8a3a525e68c80e21060c161771"}, - {file = "typing_extensions-4.7.0.tar.gz", hash = "sha256:935ccf31549830cda708b42289d44b6f74084d616a00be651601a4f968e77c82"}, + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index efdf685..2b2827a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,6 +81,7 @@ python-lsp-server = "^1.7.3" mypy = "^1.4.1" django-stubs = "^4.2.3" pylint-django = "^2.5.3" +execnet = "~1.9.0" [build-system] requires = ["poetry-core>=1.0.0"] From fb7b9e7208908847e4220ccef92ca41f93421225 Mon Sep 17 00:00:00 2001 From: Andreas Zweili Date: Mon, 10 Jul 2023 19:09:37 +0200 Subject: [PATCH 09/11] Correct setup for pylint --- flake.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index c5c268e..cf56b93 100644 --- a/flake.nix +++ b/flake.nix @@ -68,9 +68,10 @@ checkInputs = [ pkgs.inventoryDevEnv ]; checkPhase = '' mkdir -p $out - pylint --errors-only src/ + pylint --rc-file pyproject.toml -j 0 -E src/ cd src/ && mypy --config-file=../pyproject.toml . ''; + DJANGO_SETTINGS_MODULE = "network_inventory.settings.ram_test"; }; tests = pkgs.stdenv.mkDerivation { dontPatch = true; From ce3f748f336bc0f162fd033ba1664ffc27b714d6 Mon Sep 17 00:00:00 2001 From: Andreas Zweili Date: Mon, 10 Jul 2023 19:10:07 +0200 Subject: [PATCH 10/11] Disable linting for nested_admin --- src/computers/admin.py | 22 ++++++++++++---------- src/devices/admin.py | 4 ++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/computers/admin.py b/src/computers/admin.py index 3538915..ef9589a 100644 --- a/src/computers/admin.py +++ b/src/computers/admin.py @@ -26,62 +26,64 @@ from .models import ( ) -class SoftwareInLine(nested_admin.NestedStackedInline): +class SoftwareInLine(nested_admin.NestedStackedInline): # pylint: disable=no-member model = ComputerSoftwareRelation extra = 0 verbose_name_plural = "Software" -class RamInLine(nested_admin.NestedStackedInline): +class RamInLine(nested_admin.NestedStackedInline): # pylint: disable=no-member model = ComputerRamRelation extra = 0 verbose_name_plural = "RAM Modules" -class DiskInLine(nested_admin.NestedStackedInline): +class DiskInLine(nested_admin.NestedStackedInline): # pylint: disable=no-member model = ComputerDiskRelation extra = 0 verbose_name_plural = "Disks" -class DisksInRaidInLine(nested_admin.NestedStackedInline): +class DisksInRaidInLine(nested_admin.NestedStackedInline): # pylint: disable=no-member model = DisksInRaid extra = 0 verbose_name_plural = "Disks in RAID" -class CpusInLine(nested_admin.NestedStackedInline): +class CpusInLine(nested_admin.NestedStackedInline): # pylint: disable=no-member model = ComputerCpuRelation extra = 0 verbose_name_plural = "CPUs" -class GpusInLine(nested_admin.NestedStackedInline): +class GpusInLine(nested_admin.NestedStackedInline): # pylint: disable=no-member model = ComputerGpuRelation extra = 0 verbose_name_plural = "GPUs" -class RaidInLine(nested_admin.NestedStackedInline): +class RaidInLine(nested_admin.NestedStackedInline): # pylint: disable=no-member model = Raid extra = 0 verbose_name_plural = "RAID" inlines = (DisksInRaidInLine,) -class DeviceInNetInline(nested_admin.NestedStackedInline): +class DeviceInNetInline(nested_admin.NestedStackedInline): # pylint: disable=no-member model = DeviceInNet extra = 0 verbose_name_plural = "Nets" -class LicenseWithComputerInLine(nested_admin.NestedStackedInline): +class LicenseWithComputerInLine( + nested_admin.NestedStackedInline +): # pylint: disable=no-member model = LicenseWithComputer extra = 0 verbose_name_plural = "Licenses" -class ComputerAdmin(nested_admin.NestedModelAdmin): +class ComputerAdmin(nested_admin.NestedModelAdmin): # pylint: disable=no-member list_display = ("name", "host") inlines = ( SoftwareInLine, diff --git a/src/devices/admin.py b/src/devices/admin.py index 7a1f303..b790460 100644 --- a/src/devices/admin.py +++ b/src/devices/admin.py @@ -12,13 +12,13 @@ from .models import ( ) -class DeviceInNetInline(nested_admin.NestedStackedInline): +class DeviceInNetInline(nested_admin.NestedStackedInline): # pylint: disable=no-member model = DeviceInNet extra = 0 verbose_name_plural = "Nets" -class DeviceAdmin(nested_admin.NestedModelAdmin): +class DeviceAdmin(nested_admin.NestedModelAdmin): # pylint: disable=no-member inlines = (DeviceInNetInline,) From 83c1fbd93ea84b85799eeff9162779aab7c86ba4 Mon Sep 17 00:00:00 2001 From: Andreas Zweili Date: Mon, 10 Jul 2023 19:10:19 +0200 Subject: [PATCH 11/11] Make sure that we return a string --- src/customers/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/customers/models.py b/src/customers/models.py index 37e92ce..6c5f50b 100644 --- a/src/customers/models.py +++ b/src/customers/models.py @@ -43,4 +43,4 @@ class DummyLocation(models.Model): location = models.ForeignKey(Location, on_delete=models.CASCADE) def __str__(self): - return self.location + return self.location.name