diff --git a/.github/workflows/automated-checks.yml b/.github/workflows/automated-checks.yml
index 15afedf7..8b536951 100644
--- a/.github/workflows/automated-checks.yml
+++ b/.github/workflows/automated-checks.yml
@@ -3,7 +3,7 @@ name: Tests
env:
COVERAGE_DATABASE_VERSION: mariadb:10.11
COVERAGE_PYTHON_VERSION: 3.12
- NODE_VERSION: 24 # [LTS] End of Life: 30 Apr 2028 (https://endoflife.date/nodejs)
+ NODE_VERSION: 26 # [LTS] End of Life: 30 Apr 2029 (https://endoflife.date/nodejs)
REDIS_VERSION: latest
on:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index be95e44b..82c638e1 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -58,4 +58,5 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
# The name of the file(s) to upload
files: |
- dist/*
+ dist/*.tar.gz
+ dist/*.whl
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 1edda5a0..66f8d4ea 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -6,7 +6,7 @@
# Set the default language versions for the hooks
default_language_version:
python: python3 # Force all Python hooks to use Python 3
- node: 24.14.0 # Force all Node hooks to use this specific Node version
+ node: 26.3.0 # Force all Node hooks to use this specific Node version
# https://pre-commit.ci/
ci:
@@ -68,7 +68,7 @@ repos:
name: Check for large files
description: Check for large files that were added to the repository.
args:
- - --maxkb=1000
+ - --maxkb=2048 # 2 MB
- id: detect-private-key
name: Detect private key
@@ -120,8 +120,10 @@ repos:
name: End of file fixer
description: Ensure that files end with a newline.
- - repo: https://github.com/eslint/eslint
- rev: 9089853a593d6c7c0f11367da08a829e0ddfe092 # frozen: v10.6.0
+# - repo: https://github.com/eslint/eslint
+# rev: 9089853a593d6c7c0f11367da08a829e0ddfe092 # frozen: v10.6.0
+ - repo: https://github.com/pre-commit/mirrors-eslint
+ rev: 8763fc929b6232999d541a3d8eb49965a6b71afb # frozen: v10.6.0
hooks:
- id: eslint
name: ESLint
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1cb58ec5..81946f10 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -53,6 +53,7 @@ Section Order:
### Changed
+- Use `pk` instead of `id` when referring to the primary key of a model instance, since `id` is not guaranteed to be the primary key in Django.
- Migrated to Alliance Auth proxy models for `Permission`, `User` and `Group`
## [5.0.1] - 2026-07-07
diff --git a/sovtimer/models.py b/sovtimer/models.py
index 5a7045a1..87ad0455 100644
--- a/sovtimer/models.py
+++ b/sovtimer/models.py
@@ -80,10 +80,8 @@ def bulk_get_or_create_from_esi(
:rtype: dict[int, Alliance]
"""
- existing_alliances = cls.objects.filter(alliance_id__in=alliance_ids)
- existing_alliance_ids = set(
- existing_alliances.values_list("alliance_id", flat=True)
- )
+ existing_alliances = cls.objects.filter(pk__in=alliance_ids)
+ existing_alliance_ids = set(existing_alliances.values_list("pk", flat=True))
alliances_to_create = set(alliance_ids) - existing_alliance_ids
@@ -118,9 +116,9 @@ def bulk_get_or_create_from_esi(
created_alliances = cls.objects.bulk_create(new_alliances)
# Combine existing and newly created alliances into a single dictionary
- all_alliances = {
- alliance.alliance_id: alliance for alliance in existing_alliances
- } | {alliance.alliance_id: alliance for alliance in created_alliances}
+ all_alliances = {alliance.pk: alliance for alliance in existing_alliances} | {
+ alliance.pk: alliance for alliance in created_alliances
+ }
return all_alliances
diff --git a/sovtimer/tasks.py b/sovtimer/tasks.py
index 822d6cb6..2b908849 100644
--- a/sovtimer/tasks.py
+++ b/sovtimer/tasks.py
@@ -216,7 +216,7 @@ def update_sov_structures( # pylint: disable=too-many-locals
# Fetch solar systems from the database
solar_systems = {
- ss.id: ss for ss in SolarSystem.objects.filter(id__in=solar_system_ids)
+ ss.id: ss for ss in SolarSystem.objects.filter(pk__in=solar_system_ids)
}
esi_structure_ids = set() # Track structure IDs from ESI to avoid duplicates
diff --git a/sovtimer/tests/test_models.py b/sovtimer/tests/test_models.py
index 01dca9e8..85dae711 100644
--- a/sovtimer/tests/test_models.py
+++ b/sovtimer/tests/test_models.py
@@ -139,8 +139,8 @@ def test_returns_all_alliances_when_all_ids_exist(
"""
mock_existing_alliances = [
- Alliance(alliance_id=1, name="Alliance 1"),
- Alliance(alliance_id=2, name="Alliance 2"),
+ Alliance(pk=1, name="Alliance 1"),
+ Alliance(pk=2, name="Alliance 2"),
]
mock_qs = MagicMock()
mock_qs.__iter__.return_value = iter(mock_existing_alliances)
@@ -154,7 +154,7 @@ def test_returns_all_alliances_when_all_ids_exist(
self.assertEqual(len(result), 2)
self.assertEqual(result[1].name, "Alliance 1")
self.assertEqual(result[2].name, "Alliance 2")
- mock_filter.assert_called_once_with(alliance_id__in={1, 2})
+ mock_filter.assert_called_once_with(pk__in={1, 2})
mock_bulk_create.assert_not_called()
mock_get_alliance.assert_not_called()
@@ -178,7 +178,7 @@ def test_creates_new_alliances_when_some_ids_do_not_exist(
"""
mock_existing_alliances = [
- Alliance(alliance_id=1, name="Alliance 1"),
+ Alliance(pk=1, name="Alliance 1"),
]
mock_qs = MagicMock()
mock_qs.__iter__.return_value = iter(mock_existing_alliances)
@@ -192,8 +192,8 @@ def test_creates_new_alliances_when_some_ids_do_not_exist(
mock_get_alliance.side_effect = [a2, a3]
mock_bulk_create.return_value = [
- Alliance(alliance_id=2, name="Alliance 2"),
- Alliance(alliance_id=3, name="Alliance 3"),
+ Alliance(pk=2, name="Alliance 2"),
+ Alliance(pk=3, name="Alliance 3"),
]
result = Alliance.bulk_get_or_create_from_esi(
@@ -204,7 +204,7 @@ def test_creates_new_alliances_when_some_ids_do_not_exist(
self.assertEqual(result[1].name, "Alliance 1")
self.assertEqual(result[2].name, "Alliance 2")
self.assertEqual(result[3].name, "Alliance 3")
- mock_filter.assert_called_once_with(alliance_id__in={1, 2, 3})
+ mock_filter.assert_called_once_with(pk__in={1, 2, 3})
mock_get_alliance.assert_any_call(alliance_id=2, force_refresh=False)
mock_get_alliance.assert_any_call(alliance_id=3, force_refresh=False)
mock_bulk_create.assert_called_once()
@@ -239,7 +239,7 @@ def test_skips_creation_when_esi_returns_no_data(
)
self.assertEqual(len(result), 0)
- mock_filter.assert_called_once_with(alliance_id__in={1})
+ mock_filter.assert_called_once_with(pk__in={1})
mock_get_alliance.assert_called_once_with(alliance_id=1, force_refresh=False)
mock_bulk_create.assert_not_called()
diff --git a/sovtimer/tests/test_tasks.py b/sovtimer/tests/test_tasks.py
index ab08825c..80374f22 100644
--- a/sovtimer/tests/test_tasks.py
+++ b/sovtimer/tests/test_tasks.py
@@ -144,7 +144,7 @@ def test_processes_structure_with_default_vulnerability(
]
mock_bulk_get_or_create.return_value = {
- 2001: Alliance(alliance_id=2001, name="Alliance 2001")
+ 2001: Alliance(pk=2001, name="Alliance 2001")
}
mock_solar_system_filter.return_value = [SolarSystem(id=3001)]
mock_bulk_create.return_value = None
@@ -157,7 +157,7 @@ def test_processes_structure_with_default_vulnerability(
mock_bulk_get_or_create.assert_called_once_with(
alliance_ids={2001}, force_refresh=False
)
- mock_solar_system_filter.assert_called_once_with(id__in={3001})
+ mock_solar_system_filter.assert_called_once_with(pk__in={3001})
mock_bulk_create.assert_called_once()
created = mock_bulk_create.call_args[0][0]
self.assertEqual(len(created), 1)
diff --git a/sovtimer/tests/test_views.py b/sovtimer/tests/test_views.py
index a3d80c40..28bec5f3 100644
--- a/sovtimer/tests/test_views.py
+++ b/sovtimer/tests/test_views.py
@@ -147,10 +147,10 @@ def test_dashboard_data_includes_active_campaigns(
constellation = MagicMock()
constellation.name = "Constellation A"
- constellation.id = 7002
+ constellation.pk = 7002
region = MagicMock()
region.name = "Region A"
- region.id = 7001
+ region.pk = 7001
constellation.region = region
solar_system.constellation = constellation
structure.solar_system = solar_system
@@ -216,7 +216,7 @@ def test_dashboard_data_handles_missing_vulnerability_levels(
constellation.name = "Constellation B"
region = MagicMock()
region.name = "Region B"
- region.id = 7003
+ region.pk = 7003
constellation.region = region
solar_system.constellation = constellation
structure.solar_system = solar_system
@@ -288,10 +288,10 @@ def test_campaign_status_is_upcoming_when_within_timeframe(
constellation = MagicMock()
constellation.name = "Constellation U"
- constellation.id = 7004
+ constellation.pk = 7004
region = MagicMock()
region.name = "Region U"
- region.id = 7005
+ region.pk = 7005
constellation.region = region
solar_system.constellation = constellation
structure.solar_system = solar_system
@@ -354,7 +354,7 @@ def test_sets_attackers_progress_when_previous_progress_is_higher(
constellation.name = "Constellation X"
region = MagicMock()
region.name = "Region X"
- region.id = 7006
+ region.pk = 7006
constellation.region = region
solar_system.constellation = constellation
structure.solar_system = solar_system
diff --git a/sovtimer/views.py b/sovtimer/views.py
index 24c4288c..13a55396 100644
--- a/sovtimer/views.py
+++ b/sovtimer/views.py
@@ -102,8 +102,8 @@ def dashboard_data( # pylint: disable=too-many-statements too-many-locals
# Defender
defender_name = alliance.name
- defender_url = dotlan_alliance_url(eve_obj=alliance.alliance_id)
- defender_logo_url = alliance_logo_url(alliance_id=alliance.alliance_id, size=32)
+ defender_url = dotlan_alliance_url(eve_obj=alliance.pk)
+ defender_logo_url = alliance_logo_url(alliance_id=alliance.pk, size=32)
defender_name_html = (
f''
f'
{campaign_system_name}'
@@ -161,8 +161,7 @@ def dashboard_data( # pylint: disable=too-many-statements too-many-locals
f'title="{title}" data-bs-tooltip="aa-sovtimer">{icon_name}'
)
- constellation_id = constellation.id
- zkb_href = f"https://zkillboard.com/constellation/{constellation_id}/"
+ zkb_href = f"https://zkillboard.com/constellation/{constellation.pk}/"
zkb_icon = f'
'
constellation_killboard_link = (
f'