feat: use django-objects-actions

This commit is contained in:
2026-09-16 19:07:40 +02:00
parent 4392122cfc
commit f68c4e50e8
7 changed files with 62 additions and 18 deletions
+12 -2
View File
@@ -1,4 +1,9 @@
from django.contrib import admin, messages from django.contrib import admin, messages
from django_object_actions import (
DjangoObjectActions,
action,
takes_instance_or_queryset,
)
from submissions.tasks import sync_event_game_scopes_task from submissions.tasks import sync_event_game_scopes_task
@@ -19,17 +24,22 @@ class GameAdmin(admin.ModelAdmin):
@admin.register(EventGame) @admin.register(EventGame)
class EventGameAdmin(admin.ModelAdmin): class EventGameAdmin(DjangoObjectActions, admin.ModelAdmin):
list_display = ["game", "event", "enabled", "has_apibot_token"] list_display = ["game", "event", "enabled", "has_apibot_token"]
list_filter = ["event", "enabled"] list_filter = ["event", "enabled"]
search_fields = ["game__name", "event__name"] search_fields = ["game__name", "event__name"]
actions = ["sync_scopes"] actions = ["sync_scopes"]
change_actions = ["sync_scopes"]
@admin.display(description="AAndT token", boolean=True) @admin.display(description="AAndT token", boolean=True)
def has_apibot_token(self, obj): def has_apibot_token(self, obj):
return bool(obj.apibot_token) return bool(obj.apibot_token)
@admin.action(description="Sync scopes from AAndT for selected EventGames") @action(
label="Sync scopes",
description="Sync scopes from AAndT for selected EventGames",
)
@takes_instance_or_queryset
def sync_scopes(self, request, queryset): def sync_scopes(self, request, queryset):
tokened = queryset.exclude(apibot_token__isnull=True).exclude(apibot_token="") tokened = queryset.exclude(apibot_token__isnull=True).exclude(apibot_token="")
skipped = queryset.count() - tokened.count() skipped = queryset.count() - tokened.count()
+13 -5
View File
@@ -1,4 +1,9 @@
from django.contrib import admin from django.contrib import admin
from django_object_actions import (
DjangoObjectActions,
action,
takes_instance_or_queryset,
)
from market.models import Market, MarketOption, UserBet, UserPointChange from market.models import Market, MarketOption, UserBet, UserPointChange
@@ -9,7 +14,7 @@ class MarketOptionInline(admin.TabularInline):
@admin.register(Market) @admin.register(Market)
class MarketAdmin(admin.ModelAdmin): class MarketAdmin(DjangoObjectActions, admin.ModelAdmin):
list_display = ["title", "status", "end_date", "created_by", "created_at"] list_display = ["title", "status", "end_date", "created_by", "created_at"]
list_filter = ["status", "created_at"] list_filter = ["status", "created_at"]
search_fields = ["uuid", "title"] search_fields = ["uuid", "title"]
@@ -39,22 +44,25 @@ class MarketAdmin(admin.ModelAdmin):
obj.created_by = request.user obj.created_by = request.user
super().save_model(request, obj, form, change) super().save_model(request, obj, form, change)
@admin.action(description="Publish selected draft markets") actions = ["publish_markets", "close_markets"]
change_actions = ["publish_markets", "close_markets"]
@action(label="Publish", description="Publish selected draft markets")
@takes_instance_or_queryset
def publish_markets(self, request, queryset): def publish_markets(self, request, queryset):
updated = queryset.filter(status=Market.Status.DRAFT).update( updated = queryset.filter(status=Market.Status.DRAFT).update(
status=Market.Status.OPEN status=Market.Status.OPEN
) )
self.message_user(request, f"Published {updated} market(s).") self.message_user(request, f"Published {updated} market(s).")
@admin.action(description="Close selected markets") @action(label="Close", description="Close selected markets")
@takes_instance_or_queryset
def close_markets(self, request, queryset): def close_markets(self, request, queryset):
updated = queryset.filter(status=Market.Status.OPEN).update( updated = queryset.filter(status=Market.Status.OPEN).update(
status=Market.Status.CLOSED status=Market.Status.CLOSED
) )
self.message_user(request, f"Closed {updated} market(s).") self.message_user(request, f"Closed {updated} market(s).")
actions = ["publish_markets", "close_markets"]
@admin.register(MarketOption) @admin.register(MarketOption)
class MarketOptionAdmin(admin.ModelAdmin): class MarketOptionAdmin(admin.ModelAdmin):
+9 -1
View File
@@ -1,11 +1,16 @@
from django.contrib import admin from django.contrib import admin
from django_object_actions import (
DjangoObjectActions,
action,
takes_instance_or_queryset,
)
from noita.services.objectives import parse_objectives_and_store from noita.services.objectives import parse_objectives_and_store
from .models import LogfileSubmission, Objectiv, ObjectivPoint, DeathCounter from .models import LogfileSubmission, Objectiv, ObjectivPoint, DeathCounter
@admin.register(LogfileSubmission) @admin.register(LogfileSubmission)
class LogfileSubmissionAdmin(admin.ModelAdmin): class LogfileSubmissionAdmin(DjangoObjectActions, admin.ModelAdmin):
list_display = ( list_display = (
"id", "id",
"user", "user",
@@ -26,7 +31,10 @@ class LogfileSubmissionAdmin(admin.ModelAdmin):
) )
actions = ["validate_submission"] actions = ["validate_submission"]
change_actions = ["validate_submission"]
@action(label="Validate", description="Validate selected submissions")
@takes_instance_or_queryset
def validate_submission(self, request, queryset): def validate_submission(self, request, queryset):
for logfile in queryset: for logfile in queryset:
parse_objectives_and_store(logfile) parse_objectives_and_store(logfile)
+15 -10
View File
@@ -1,6 +1,11 @@
from django.contrib import admin from django.contrib import admin
from django.utils.html import format_html from django.utils.html import format_html
from django.utils import timezone from django.utils import timezone
from django_object_actions import (
DjangoObjectActions,
action,
takes_instance_or_queryset,
)
from opus_magnum.models import ( from opus_magnum.models import (
SteamAPIKey, SteamAPIKey,
SteamCollection, SteamCollection,
@@ -185,7 +190,7 @@ class PuzzleResponseInline(admin.TabularInline):
@admin.register(Submission) @admin.register(Submission)
class SubmissionAdmin(admin.ModelAdmin): class SubmissionAdmin(DjangoObjectActions, admin.ModelAdmin):
list_display = [ list_display = [
"id", "id",
"user", "user",
@@ -238,9 +243,11 @@ class SubmissionAdmin(admin.ModelAdmin):
) )
actions = ["mark_as_validated"] actions = ["mark_as_validated"]
change_actions = ["mark_as_validated"]
@action(label="Validate", description="Mark selected submissions as validated")
@takes_instance_or_queryset
def mark_as_validated(self, request, queryset): def mark_as_validated(self, request, queryset):
"""Mark selected submissions as validated"""
updated = 0 updated = 0
for submission in queryset: for submission in queryset:
if not submission.is_validated: if not submission.is_validated:
@@ -254,11 +261,9 @@ class SubmissionAdmin(admin.ModelAdmin):
self.message_user(request, f"{updated} submissions marked as validated.") self.message_user(request, f"{updated} submissions marked as validated.")
mark_as_validated.short_description = "Mark selected submissions as validated"
@admin.register(PuzzleResponse) @admin.register(PuzzleResponse)
class PuzzleResponseAdmin(admin.ModelAdmin): class PuzzleResponseAdmin(DjangoObjectActions, admin.ModelAdmin):
list_display = [ list_display = [
"puzzle_name", "puzzle_name",
"submission", "submission",
@@ -314,22 +319,22 @@ class PuzzleResponseAdmin(admin.ModelAdmin):
) )
actions = ["mark_for_validation", "clear_validation_flag"] actions = ["mark_for_validation", "clear_validation_flag"]
change_actions = ["mark_for_validation", "clear_validation_flag"]
@action(label="Mark for validation", description="Mark as needing validation")
@takes_instance_or_queryset
def mark_for_validation(self, request, queryset): def mark_for_validation(self, request, queryset):
"""Mark selected responses as needing validation"""
updated = queryset.update(needs_manual_validation=True) updated = queryset.update(needs_manual_validation=True)
self.message_user(request, f"{updated} responses marked for validation.") self.message_user(request, f"{updated} responses marked for validation.")
@action(label="Clear validation flag", description="Clear validation flag")
@takes_instance_or_queryset
def clear_validation_flag(self, request, queryset): def clear_validation_flag(self, request, queryset):
"""Clear validation flag for selected responses"""
updated = queryset.update(needs_manual_validation=False) updated = queryset.update(needs_manual_validation=False)
self.message_user( self.message_user(
request, f"{updated} responses cleared from validation queue." request, f"{updated} responses cleared from validation queue."
) )
mark_for_validation.short_description = "Mark as needing validation"
clear_validation_flag.short_description = "Clear validation flag"
@admin.register(SubmissionFile) @admin.register(SubmissionFile)
class SubmissionFileAdmin(admin.ModelAdmin): class SubmissionFileAdmin(admin.ModelAdmin):
@@ -45,6 +45,7 @@ INSTALLED_APPS = [
"django_vite", "django_vite",
"django_rq", "django_rq",
"django_tasks_rq", "django_tasks_rq",
"django_object_actions",
"accounts", "accounts",
"events", "events",
"animations", "animations",
+1
View File
@@ -25,6 +25,7 @@ dependencies = [
"django-rq", "django-rq",
"django-tasks-rq", "django-tasks-rq",
"httpx>=0.28.1", "httpx>=0.28.1",
"django-object-actions>=5.1.2",
] ]
[dependency-groups] [dependency-groups]
Generated
+11
View File
@@ -677,6 +677,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/64/96/d967ca440d6a8e3861120f51985d8e5aec79b9a8bdda16041206adfe7adc/django_extensions-4.1-py3-none-any.whl", hash = "sha256:0699a7af28f2523bf8db309a80278519362cd4b6e1fd0a8cd4bf063e1e023336", size = 232980, upload-time = "2025-04-11T01:15:37.701Z" }, { url = "https://files.pythonhosted.org/packages/64/96/d967ca440d6a8e3861120f51985d8e5aec79b9a8bdda16041206adfe7adc/django_extensions-4.1-py3-none-any.whl", hash = "sha256:0699a7af28f2523bf8db309a80278519362cd4b6e1fd0a8cd4bf063e1e023336", size = 232980, upload-time = "2025-04-11T01:15:37.701Z" },
] ]
[[package]]
name = "django-object-actions"
version = "5.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c4/2f/99e8fa3d117becebfbe46d54d0b85d81475b6375b85507907a7aaf1d5d2b/django_object_actions-5.1.2.tar.gz", hash = "sha256:a11ac544e5beb6b309a50e184a2086e1e18f6469704d9609bbe083a8972a2245", size = 19048, upload-time = "2026-05-28T05:48:29.88Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/37/23/c5743780fc68433f244fba86cd64b513b9d6a9491714ac700a4654b5e00f/django_object_actions-5.1.2-py3-none-any.whl", hash = "sha256:366dcd5ccfb3ef6a16ce4c147be8df2a35b52d637eb36fb9c558e28419ae4038", size = 16900, upload-time = "2026-05-28T05:48:28.56Z" },
]
[[package]] [[package]]
name = "django-redis" name = "django-redis"
version = "6.0.0" version = "6.0.0"
@@ -1626,6 +1635,7 @@ dependencies = [
{ name = "django", version = "5.2.14", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, { name = "django", version = "5.2.14", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "django", version = "6.0.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "django", version = "6.0.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "django-channels" }, { name = "django-channels" },
{ name = "django-object-actions" },
{ name = "django-redis" }, { name = "django-redis" },
{ name = "django-rq" }, { name = "django-rq" },
{ name = "django-shinobi" }, { name = "django-shinobi" },
@@ -1666,6 +1676,7 @@ requires-dist = [
{ name = "daphne" }, { name = "daphne" },
{ name = "django" }, { name = "django" },
{ name = "django-channels" }, { name = "django-channels" },
{ name = "django-object-actions", specifier = ">=5.1.2" },
{ name = "django-redis" }, { name = "django-redis" },
{ name = "django-rq" }, { name = "django-rq" },
{ name = "django-shinobi" }, { name = "django-shinobi" },