feat: add events app + "multi-tenants" aka multi-events

This commit is contained in:
2026-09-16 00:03:26 +02:00
parent b5c2cdc037
commit 8b9c254fd2
62 changed files with 1343 additions and 362 deletions
+13 -18
View File
@@ -1,28 +1,23 @@
from django.contrib import admin
from .models import Game
from .models import EventGame, Game
def enable_games(modeladmin, request, queryset):
updated = queryset.update(enabled=True)
modeladmin.message_user(request, f"{updated} game(s) enabled successfully.")
enable_games.short_description = "Enable selected games"
def disable_games(modeladmin, request, queryset):
updated = queryset.update(enabled=False)
modeladmin.message_user(request, f"{updated} game(s) disabled successfully.")
disable_games.short_description = "Disable selected games"
class EventGameInline(admin.TabularInline):
model = EventGame
extra = 0
@admin.register(Game)
class GameAdmin(admin.ModelAdmin):
list_display = ["name", "steam_app_id", "enabled", "updated_at"]
list_filter = ["enabled"]
list_display = ["name", "steam_app_id", "updated_at"]
search_fields = ["name", "steam_app_id"]
readonly_fields = ["created_at", "updated_at"]
actions = [enable_games, disable_games]
inlines = [EventGameInline]
@admin.register(EventGame)
class EventGameAdmin(admin.ModelAdmin):
list_display = ["game", "event", "enabled"]
list_filter = ["event", "enabled"]
search_fields = ["game__name", "event__name"]
+7 -1
View File
@@ -6,8 +6,14 @@ from .models import Game
from .schemas import GameOut
router = Router()
event_router = Router()
@router.get("", response=List[GameOut])
def list_games(request):
return Game.objects.filter(enabled=True)
return Game.objects.all()
@event_router.get("/events/{event_path}/games", response=List[GameOut])
def list_event_games(request, event_path: str):
return Game.objects.enabled_for_event(request.event)
+10 -5
View File
@@ -1,7 +1,9 @@
from functools import wraps
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404
from events.models import Event
from .models import Game
@@ -9,12 +11,15 @@ def require_game_enabled(steam_app_id: int):
def decorator(view_func):
@wraps(view_func)
def wrapper(request, *args, **kwargs):
try:
game = Game.objects.get(steam_app_id=steam_app_id)
except Game.DoesNotExist:
raise PermissionDenied
if not game.enabled:
event = get_object_or_404(Event, path=kwargs.get("event_path"))
if (
not Game.objects.enabled_for_event(event)
.filter(steam_app_id=steam_app_id)
.exists()
):
raise PermissionDenied
return view_func(request, *args, **kwargs)
return wrapper
@@ -0,0 +1,52 @@
# Generated by Django 6.0.5 on 2026-09-15 21:11
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("events", "0002_seed_polylan_43"),
("games", "0004_game_image_alter_game_steam_app_id"),
]
operations = [
migrations.CreateModel(
name="EventGame",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("enabled", models.BooleanField(default=True)),
(
"event",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="event_games",
to="events.event",
),
),
(
"game",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="event_games",
to="games.game",
),
),
],
options={
"constraints": [
models.UniqueConstraint(
fields=("event", "game"), name="unique_event_game"
)
],
},
),
]
@@ -0,0 +1,16 @@
# Generated by Django 6.0.5 on 2026-09-15 21:51
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("games", "0005_eventgame"),
]
operations = [
migrations.RemoveField(
model_name="game",
name="enabled",
),
]
+34 -1
View File
@@ -1,16 +1,24 @@
from django.db import models
from events.models import Event
class GameQuerySet(models.QuerySet):
def enabled_for_event(self, event):
return self.exclude(event_games__event=event, event_games__enabled=False)
class Game(models.Model):
steam_app_id = models.PositiveIntegerField(null=True, blank=True)
name = models.CharField(max_length=255)
path = models.CharField(max_length=100)
image = models.URLField(null=True, blank=True)
enabled = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = models.Manager.from_queryset(GameQuerySet)()
class Meta:
ordering = ["name"]
@@ -18,3 +26,28 @@ class Game(models.Model):
if self.steam_app_id:
return f"{self.name} ({self.steam_app_id})"
return self.name
class EventGame(models.Model):
event = models.ForeignKey(
Event,
on_delete=models.CASCADE,
related_name="event_games",
)
game = models.ForeignKey(
Game,
on_delete=models.CASCADE,
related_name="event_games",
)
enabled = models.BooleanField(default=True)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["event", "game"],
name="unique_event_game",
),
]
def __str__(self) -> str:
return f"{self.game.name} @ {self.event.name} ({'enabled' if self.enabled else 'disabled'})"