From 2d2e17eb7032a60828eac2d9131f9c9a418069aa Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 11 Nov 2024 17:39:49 +0100 Subject: [PATCH] Adds related_name for inverse relationship + ViewSet now use the parameter store_id to filter by store_id/tournament --- api/views.py | 47 ++- tournaments/models/base.py | 46 ++- tournaments/models/club.py | 6 +- tournaments/models/court.py | 2 +- tournaments/models/custom_user.py | 8 +- tournaments/models/date_interval.py | 2 +- tournaments/models/event.py | 10 +- tournaments/models/failed_api_call.py | 2 +- tournaments/models/group_stage.py | 15 +- tournaments/models/log.py | 2 +- tournaments/models/match.py | 9 +- tournaments/models/model_log.py | 2 +- tournaments/models/player_registration.py | 2 +- tournaments/models/purchase.py | 2 +- tournaments/models/round.py | 11 +- tournaments/models/team_registration.py | 17 +- tournaments/models/team_score.py | 2 +- tournaments/models/tournament.py | 70 ++-- tournaments/models/views.py | 472 ---------------------- tournaments/views.py | 4 +- 20 files changed, 139 insertions(+), 592 deletions(-) delete mode 100644 tournaments/models/views.py diff --git a/api/views.py b/api/views.py index 97d3755..b14db3e 100644 --- a/api/views.py +++ b/api/views.py @@ -1,5 +1,5 @@ from .serializers import ClubSerializer, CourtSerializer, DateIntervalSerializer, TournamentSerializer, UserSerializer, ChangePasswordSerializer, EventSerializer, RoundSerializer, GroupStageSerializer, MatchSerializer, TeamScoreSerializer, TeamRegistrationSerializer, PlayerRegistrationSerializer, LiveMatchSerializer, PurchaseSerializer, CustomUserSerializer, FailedApiCallSerializer, LogSerializer, DeviceTokenSerializer, DataAccessSerializer -from tournaments.models import Club, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamScore, TeamRegistration, PlayerRegistration, Court, DateInterval, Purchase, FailedApiCall, Log, DeviceToken, ModelLog, DataAccess +from tournaments.models import Club, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamScore, TeamRegistration, PlayerRegistration, Court, DateInterval, Purchase, FailedApiCall, Log, DeviceToken, ModelLog, DataAccess, BaseModel from rest_framework import viewsets, permissions from rest_framework.authtoken.models import Token @@ -131,7 +131,8 @@ class DataApi(APIView): except ValueError: return Response({"error": f"Invalid date format for last_update: {last_update}"}, status=status.HTTP_400_BAD_REQUEST) - data_access = DataAccess.objects.filter(shared_with=request.user).values_list('model_id', flat=True) + data_access_query = Q(shared_with=request.user) | Q(owner=request.user) + data_access = DataAccess.objects.filter(data_access_query).values_list('model_id', flat=True) log_query = Q(date__gt=last_update) & (Q(user=request.user) | Q(model_id__in=data_access) | Q(parent_model_id__in=data_access)) logs = ModelLog.objects.filter(log_query).order_by('date') @@ -157,6 +158,7 @@ class DataApi(APIView): updates[log.model_name][log.model_id] = data instance = model.objects.get(id=log.model_id) self.add_children_recursively(instance, updates) + self.add_parents_recursively(instance, updates) elif log.operation == 'REVOKE_ACCESS': print(f'revoke access {log.model_id} - {log.store_id}') # data = self.get_data(model, log) @@ -184,19 +186,26 @@ class DataApi(APIView): """ Recursively add all children of an instance to the updates dictionary. """ - child_models = instance.get_child_models() - - for child_model_name, related_name in child_models.items(): - child_model = apps.get_model(app_label='tournaments', model_name=child_model_name) - children = getattr(instance, related_name).all() + child_models = instance.get_children_by_model() + for child_model_name, children in child_models.items(): for child in children: - serializer_class = build_serializer_class(child_model_name) - serializer = serializer_class(child) - updates[child_model_name][child.id] = serializer.data - - # Recursive call for each child - self.add_children_recursively(child, updates) + if isinstance(child, BaseModel): + serializer_class = build_serializer_class(child_model_name) + serializer = serializer_class(child) + updates[child_model_name][child.id] = serializer.data + self.add_children_recursively(child, updates) + + def add_parents_recursively(self, instance, updates): + parent_models = instance.get_parents_by_model() + + for parent_model_name, parent in parent_models.items(): + # print(f'parent = {parent_model_name}') + if isinstance(parent, BaseModel): + serializer_class = build_serializer_class(parent_model_name) + serializer = serializer_class(parent) + updates[parent_model_name][parent.id] = serializer.data + self.add_parents_recursively(parent, updates) def get_data(self, model, log): instance = model.objects.get(id=log.model_id) @@ -357,7 +366,7 @@ class RoundViewSet(viewsets.ModelViewSet): serializer_class = RoundSerializer def get_queryset(self): - tournament_id = self.request.query_params.get('tournament') + tournament_id = self.request.query_params.get('store_id') if tournament_id: return self.queryset.filter(tournament=tournament_id) if self.request.user: @@ -369,7 +378,7 @@ class GroupStageViewSet(viewsets.ModelViewSet): serializer_class = GroupStageSerializer def get_queryset(self): - tournament_id = self.request.query_params.get('tournament') + tournament_id = self.request.query_params.get('store_id') if tournament_id: return self.queryset.filter(tournament=tournament_id) if self.request.user: @@ -381,7 +390,7 @@ class MatchViewSet(viewsets.ModelViewSet): serializer_class = MatchSerializer def get_queryset(self): - tournament_id = self.request.query_params.get('tournament') + tournament_id = self.request.query_params.get('store_id') if tournament_id: return self.queryset.filter(Q(group_stage__tournament=tournament_id) | Q(round__tournament=tournament_id)) if self.request.user: @@ -393,7 +402,7 @@ class TeamScoreViewSet(viewsets.ModelViewSet): serializer_class = TeamScoreSerializer def get_queryset(self): - tournament_id = self.request.query_params.get('tournament') + tournament_id = self.request.query_params.get('store_id') if tournament_id: return self.queryset.filter(team_registration__tournament=tournament_id) if self.request.user: @@ -405,7 +414,7 @@ class TeamRegistrationViewSet(viewsets.ModelViewSet): serializer_class = TeamRegistrationSerializer def get_queryset(self): - tournament_id = self.request.query_params.get('tournament') + tournament_id = self.request.query_params.get('store_id') if tournament_id: return self.queryset.filter(tournament=tournament_id) if self.request.user: @@ -417,7 +426,7 @@ class PlayerRegistrationViewSet(viewsets.ModelViewSet): serializer_class = PlayerRegistrationSerializer def get_queryset(self): - tournament_id = self.request.query_params.get('tournament') + tournament_id = self.request.query_params.get('store_id') if tournament_id: return self.queryset.filter(team_registration__tournament=tournament_id) if self.request.user: diff --git a/tournaments/models/base.py b/tournaments/models/base.py index a506f08..262e364 100644 --- a/tournaments/models/base.py +++ b/tournaments/models/base.py @@ -12,8 +12,50 @@ class BaseModel(models.Model): """Override in child models to provide parent reference""" return None, None - def get_child_models(self): - return {} + def get_children_by_model(self): + """ + Returns a dictionary where: + - keys are the model names + - values are QuerySets of related objects + """ + children = self._meta.get_fields(include_parents=False, include_hidden=False) + related_objects = {} + + for child in children: + if (child.one_to_many or child.one_to_one) and child.auto_created: + model_name = child.related_model.__name__ + related_objects[model_name] = getattr(self, child.name).all() + + return related_objects + + def get_parents_by_model(self): + """ + Returns a dictionary where: + - keys are the model names + - values are the parent model instances + """ + parents = {} + + # Get all fields including parent links + fields = self._meta.get_fields(include_parents=True, include_hidden=True) + + for field in fields: + # Check if the field is a parent link (OneToOne field that points to a parent model) + if isinstance(field, models.OneToOneRel) and field.parent_link: + model_name = field.related_model.__name__ + # Get the parent instance using the related name + parent_instance = getattr(self, field.get_accessor_name()) + if parent_instance: + parents[model_name] = parent_instance + + # Also check for direct foreign key relationships that might represent parent relationships + elif isinstance(field, models.ForeignKey): + model_name = field.related_model.__name__ + parent_instance = getattr(self, field.name) + if parent_instance: + parents[model_name] = parent_instance + + return parents class SideStoreModel(BaseModel): store_id = models.CharField(max_length=100) diff --git a/tournaments/models/club.py b/tournaments/models/club.py index 74321e9..0e1bc18 100644 --- a/tournaments/models/club.py +++ b/tournaments/models/club.py @@ -4,7 +4,7 @@ from . import BaseModel class Club(BaseModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) - creator = models.ForeignKey('CustomUser', blank=True, null=True, on_delete=models.SET_NULL) # string to avoid circular import + creator = models.ForeignKey('CustomUser', blank=True, null=True, on_delete=models.SET_NULL) name = models.CharField(max_length=50) acronym = models.CharField(max_length=10) phone = models.CharField(max_length=15, null=True, blank=True) @@ -24,10 +24,10 @@ class Club(BaseModel): return self.name def events_count(self): - return len(self.event_set.all()) + return len(self.events.all()) def court_name(self, index): - for court in self.court_set.all(): + for court in self.courts.all(): if court.index == index and court.name is not None and len(court.name) > 0: return court.name diff --git a/tournaments/models/court.py b/tournaments/models/court.py index eea8dd4..c5dc8bf 100644 --- a/tournaments/models/court.py +++ b/tournaments/models/court.py @@ -5,7 +5,7 @@ from . import BaseModel, Club class Court(BaseModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) index = models.IntegerField(default=0) - club = models.ForeignKey(Club, on_delete=models.CASCADE) + club = models.ForeignKey(Club, on_delete=models.CASCADE, related_name='courts') name = models.CharField(max_length=50, null=True, blank=True) exit_allowed = models.BooleanField(default=False) indoor = models.BooleanField(default=False) diff --git a/tournaments/models/custom_user.py b/tournaments/models/custom_user.py index 0629b0c..e52e79f 100644 --- a/tournaments/models/custom_user.py +++ b/tournaments/models/custom_user.py @@ -11,7 +11,7 @@ class CustomUser(AbstractUser): last_update = models.DateTimeField(default=now) email = models.EmailField(unique=True) umpire_code = models.CharField(max_length=50, blank=True, null=True) - clubs = models.ManyToManyField(club.Club, blank=True) + clubs = models.ManyToManyField(club.Club, blank=True, related_name='creators') phone = models.CharField(max_length=15, null=True, blank=True) first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) @@ -47,6 +47,10 @@ class CustomUser(AbstractUser): 'summons_use_full_custom_message', 'match_formats_default_duration', 'bracket_match_format_preference', 'group_stage_match_format_preference', 'loser_bracket_match_format_preference', 'device_id'] + def get_parent_reference(self): + """Override in child models to provide parent reference""" + return None, None + def __str__(self): return self.username @@ -54,7 +58,7 @@ class CustomUser(AbstractUser): return f"{self.username} : {self.first_name} {self.last_name} | {self.email} | {self.phone}" def event_count(self): - return len(self.event_set.all()) + return len(self.events.all()) def full_name(self): return f"{self.first_name} {self.last_name}" diff --git a/tournaments/models/date_interval.py b/tournaments/models/date_interval.py index bf6871d..56d4696 100644 --- a/tournaments/models/date_interval.py +++ b/tournaments/models/date_interval.py @@ -4,7 +4,7 @@ from . import BaseModel class DateInterval(BaseModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) - event = models.ForeignKey('Event', on_delete=models.CASCADE) + event = models.ForeignKey('Event', on_delete=models.CASCADE, related_name='date_intervals') court_index = models.IntegerField() start_date = models.DateTimeField() end_date = models.DateTimeField() diff --git a/tournaments/models/event.py b/tournaments/models/event.py index f4e4341..da66662 100644 --- a/tournaments/models/event.py +++ b/tournaments/models/event.py @@ -4,20 +4,14 @@ import uuid class Event(BaseModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) - club = models.ForeignKey(Club, on_delete=models.SET_NULL, null=True, blank=True) + club = models.ForeignKey(Club, on_delete=models.SET_NULL, null=True, blank=True, related_name='events') creation_date = models.DateTimeField() - creator = models.ForeignKey(CustomUser, blank=True, null=True, on_delete=models.SET_NULL) + creator = models.ForeignKey(CustomUser, blank=True, null=True, on_delete=models.SET_NULL, related_name='events') name = models.CharField(max_length=200, null=True, blank=True) federal_tournament_data = models.JSONField(null=True, blank=True) tenup_id = models.CharField(max_length=20, null=True, blank=True) creator_full_name = models.CharField(max_length=200, null=True, blank=True) - # Data Access - def get_child_models(self): - return { - 'Tournament': 'tournament_set', - } - def __str__(self): return self.display_name() diff --git a/tournaments/models/failed_api_call.py b/tournaments/models/failed_api_call.py index 4ad2e94..04dc297 100644 --- a/tournaments/models/failed_api_call.py +++ b/tournaments/models/failed_api_call.py @@ -5,7 +5,7 @@ import uuid class FailedApiCall(BaseModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) date = models.DateTimeField() - user = models.ForeignKey(CustomUser, blank=True, null=True, on_delete=models.SET_NULL) + user = models.ForeignKey(CustomUser, blank=True, null=True, on_delete=models.SET_NULL, related_name='failed_api_calls') type = models.CharField(max_length=50) call_id = models.UUIDField() error = models.TextField() diff --git a/tournaments/models/group_stage.py b/tournaments/models/group_stage.py index 91be198..3012c58 100644 --- a/tournaments/models/group_stage.py +++ b/tournaments/models/group_stage.py @@ -8,7 +8,7 @@ from django.utils import timezone class GroupStage(SideStoreModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) - tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE) + tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE, related_name='group_stages') index = models.IntegerField(default=0) size = models.IntegerField(default=4) format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) @@ -19,11 +19,6 @@ class GroupStage(SideStoreModel): def get_parent_reference(self): return 'Event', self.tournament.event.id - def get_child_models(self): - return { - 'Match': 'match_set', - } - def __str__(self): return self.display_name() # return f"{self.tournament.display_name()} - {self.display_name()}" @@ -50,7 +45,7 @@ class GroupStage(SideStoreModel): gs_teams = {} # init all teams - for team_registration in self.teamregistration_set.all(): + for team_registration in self.team_registrations.all(): if team_registration in gs_teams: team = gs_teams[team_registration] else: @@ -58,7 +53,7 @@ class GroupStage(SideStoreModel): gs_teams[team_registration] = team # compute matches - for match in self.match_set.all(): + for match in self.matches.all(): lgs.add_match(match) team_scores = match.team_scores.all() if len(team_scores) == 2: @@ -99,7 +94,7 @@ class GroupStage(SideStoreModel): team1.diff += total1 - total2 team2.diff += total2 - total1 - if len(self.match_set.filter(end_date__isnull=False).all()) > 0: + if len(self.matches.filter(end_date__isnull=False).all()) > 0: teams = sorted(gs_teams.values(), key=lambda team: -(team.wins * 100 + team.diff)) else: teams = sorted(gs_teams.values(), key=lambda team: team.position) @@ -116,7 +111,7 @@ class GroupStage(SideStoreModel): return False def has_at_least_one_started_match(self): - for match in self.match_set.all(): + for match in self.matches.all(): if match.start_date: return True return False diff --git a/tournaments/models/log.py b/tournaments/models/log.py index 4ecc756..30577f9 100644 --- a/tournaments/models/log.py +++ b/tournaments/models/log.py @@ -5,7 +5,7 @@ import uuid class Log(BaseModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) date = models.DateTimeField() - user = models.ForeignKey(CustomUser, blank=True, null=True, on_delete=models.SET_NULL) + user = models.ForeignKey(CustomUser, blank=True, null=True, on_delete=models.SET_NULL, related_name='logs') message = models.TextField(blank=True, null=True) def __str__(self): diff --git a/tournaments/models/match.py b/tournaments/models/match.py index 870c378..c058a53 100644 --- a/tournaments/models/match.py +++ b/tournaments/models/match.py @@ -9,8 +9,8 @@ from ..utils.extensions import format_seconds class Match(SideStoreModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) - round = models.ForeignKey(Round, null=True, blank=True, on_delete=models.CASCADE) - group_stage = models.ForeignKey(GroupStage, null=True, blank=True, on_delete=models.CASCADE) + round = models.ForeignKey(Round, null=True, blank=True, on_delete=models.CASCADE, related_name='matches') + group_stage = models.ForeignKey(GroupStage, null=True, blank=True, on_delete=models.CASCADE, related_name='matches') name = models.CharField(max_length=200, null=True, blank=True) start_date = models.DateTimeField(null=True, blank=True) end_date = models.DateTimeField(null=True, blank=True) @@ -30,11 +30,6 @@ class Match(SideStoreModel): def get_parent_reference(self): return 'Event', self.tournament().event.id - def get_child_models(self): - return { - 'TeamScore': 'team_scores', - } - def __str__(self): names = " / ".join(self.player_names()) return f"{self.stage_name()} #{self.index}: {names}" diff --git a/tournaments/models/model_log.py b/tournaments/models/model_log.py index 46835dd..77fcdd6 100644 --- a/tournaments/models/model_log.py +++ b/tournaments/models/model_log.py @@ -4,7 +4,7 @@ from . import ModelOperation class ModelLog(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) - user = models.ForeignKey('CustomUser', blank=True, null=True, on_delete=models.SET_NULL) + user = models.ForeignKey('CustomUser', blank=True, null=True, on_delete=models.SET_NULL, related_name='model_logs') model_id = models.UUIDField() operation = models.CharField(choices=ModelOperation.choices, max_length=50) date = models.DateTimeField() diff --git a/tournaments/models/player_registration.py b/tournaments/models/player_registration.py index 869bb07..e087425 100644 --- a/tournaments/models/player_registration.py +++ b/tournaments/models/player_registration.py @@ -4,7 +4,7 @@ import uuid class PlayerRegistration(SideStoreModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) - team_registration = models.ForeignKey(TeamRegistration, on_delete=models.CASCADE) + team_registration = models.ForeignKey(TeamRegistration, on_delete=models.CASCADE, related_name='player_registrations') first_name = models.CharField(max_length=50, blank=True) last_name = models.CharField(max_length=50, blank=True) licence_id = models.CharField(max_length=20, null=True, blank=True) diff --git a/tournaments/models/purchase.py b/tournaments/models/purchase.py index a1fb4d0..c139ef4 100644 --- a/tournaments/models/purchase.py +++ b/tournaments/models/purchase.py @@ -4,7 +4,7 @@ from . import BaseModel, CustomUser class Purchase(BaseModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) - user = models.ForeignKey(CustomUser, on_delete=models.CASCADE) + user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='purchases') identifier = models.BigIntegerField() purchase_date = models.DateTimeField() product_id = models.CharField(max_length=100) diff --git a/tournaments/models/round.py b/tournaments/models/round.py index de4756f..c94582b 100644 --- a/tournaments/models/round.py +++ b/tournaments/models/round.py @@ -4,7 +4,7 @@ import uuid class Round(SideStoreModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) - tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE) + tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE, related_name='rounds') index = models.IntegerField(default=0) parent = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE, related_name='children') format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) @@ -14,11 +14,6 @@ class Round(SideStoreModel): def get_parent_reference(self): return 'Event', self.tournament.event.id - def get_child_models(self): - return { - 'Match': 'match_set', - } - def __str__(self): if self.parent: return f"LB: {self.name()}" @@ -50,7 +45,7 @@ class Round(SideStoreModel): matches = [] for child in self.children.all(): - child_matches = child.match_set.all() + child_matches = child.matches.all() if hide_empty_matches: child_matches = [m for m in child_matches if m.should_appear()] else: @@ -66,7 +61,7 @@ class Round(SideStoreModel): return matches def get_matches_recursive(self, hide_empty_matches): - matches = list(self.match_set.all()) # Retrieve matches associated with the current round + matches = list(self.matches.all()) # Retrieve matches associated with the current round matches = [m for m in matches if m.should_appear()] if hide_empty_matches: matches = [m for m in matches if m.should_appear()] diff --git a/tournaments/models/team_registration.py b/tournaments/models/team_registration.py index 8d48b89..44a56cb 100644 --- a/tournaments/models/team_registration.py +++ b/tournaments/models/team_registration.py @@ -6,8 +6,8 @@ from django.utils import timezone class TeamRegistration(SideStoreModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) - tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE) - group_stage = models.ForeignKey(GroupStage, null=True, blank=True, on_delete=models.SET_NULL) + tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE, related_name='team_registrations') + group_stage = models.ForeignKey(GroupStage, null=True, blank=True, on_delete=models.SET_NULL, related_name='team_registrations') registration_date = models.DateTimeField(null=True, blank=True) call_date = models.DateTimeField(null=True, blank=True) bracket_position = models.IntegerField(null=True, blank=True) @@ -34,11 +34,6 @@ class TeamRegistration(SideStoreModel): def get_parent_reference(self): return 'Event', self.tournament.event.id - def get_child_models(self): - return { - 'PlayerRegistration': 'playerregistration_set', - } - def __str__(self): if self.name: return self.name @@ -52,16 +47,16 @@ class TeamRegistration(SideStoreModel): if self.name: return [self.name] else: - return [pr.name() for pr in self.playerregistration_set.all()] + return [pr.name() for pr in self.player_registrations.all()] def shortened_team_names(self): if self.name: return [self.name] else: - return [pr.shortened_name() for pr in self.playerregistration_set.all()] + return [pr.shortened_name() for pr in self.player_registrations.all()] def player_names(self): - names = [pr.name() for pr in self.playerregistration_set.all()] + names = [pr.name() for pr in self.player_registrations.all()] str = " - ".join(names) if len(str) > 0: return str @@ -94,7 +89,7 @@ class TeamRegistration(SideStoreModel): return "--" def is_valid_for_summon(self): - return len(self.playerregistration_set.all()) > 0 + return len(self.player_registrations.all()) > 0 def initial_weight(self): if self.locked_weight is None: diff --git a/tournaments/models/team_score.py b/tournaments/models/team_score.py index 4dbd00b..b032554 100644 --- a/tournaments/models/team_score.py +++ b/tournaments/models/team_score.py @@ -5,7 +5,7 @@ import uuid class TeamScore(SideStoreModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) match = models.ForeignKey(Match, on_delete=models.CASCADE, related_name="team_scores") - team_registration = models.ForeignKey(TeamRegistration, on_delete=models.CASCADE, null=True, blank=True) + team_registration = models.ForeignKey(TeamRegistration, on_delete=models.CASCADE, null=True, blank=True, related_name="team_scores") score = models.CharField(max_length=50, null=True, blank=True) walk_out = models.IntegerField(null=True, blank=True) # TODO type of WO: forfeit, injury... lucky_loser = models.IntegerField(null=True, blank=True) diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py index 1076bc7..588d77d 100644 --- a/tournaments/models/tournament.py +++ b/tournaments/models/tournament.py @@ -17,7 +17,7 @@ class TeamSortingType(models.IntegerChoices): class Tournament(BaseModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) - event = models.ForeignKey(Event, blank=True, null=True, on_delete=models.CASCADE) + event = models.ForeignKey(Event, blank=True, null=True, on_delete=models.CASCADE, related_name='tournaments') name = models.CharField(max_length=200, null=True, blank=True) start_date = models.DateTimeField() end_date = models.DateTimeField(null=True, blank=True) @@ -63,16 +63,6 @@ class Tournament(BaseModel): def get_parent_reference(self): return 'Event', self.event.id - def get_child_models(self): - """ - Returns a dictionary of child model names and their related_names. - """ - return { - 'Round': 'round_set', - 'GroupStage': 'groupstage_set', - 'TeamRegistration': 'teamregistration_set' - } - def __str__(self): if self.name: return self.name @@ -206,7 +196,7 @@ class Tournament(BaseModel): def team_summons(self): summons = [] - for team_registration in self.teamregistration_set.all(): + for team_registration in self.team_registrations.all(): if team_registration.is_valid_for_summon(): next_match = team_registration.next_match() if next_match and next_match.start_date is not None: @@ -221,7 +211,7 @@ class Tournament(BaseModel): def rankings(self): rankings = [] - for team_registration in self.teamregistration_set.all(): + for team_registration in self.team_registrations.all(): if team_registration.walk_out is False and team_registration.final_ranking is not None: names = team_registration.team_names() ranking = team_registration.final_ranking @@ -242,7 +232,7 @@ class Tournament(BaseModel): complete_teams = [] closed_registration_date = self.closed_registration_date - for team_registration in self.teamregistration_set.all(): + for team_registration in self.team_registrations.all(): is_valid = False if closed_registration_date is not None and team_registration.registration_date is not None and team_registration.registration_date <= closed_registration_date: is_valid = True @@ -339,10 +329,10 @@ class Tournament(BaseModel): match_groups = [] if group_stage_id: - group_stage = self.groupstage_set.filter(id=group_stage_id).first() + group_stage = self.group_stages.filter(id=group_stage_id).first() match_groups.append(self.group_stage_match_group(group_stage, broadcasted, hide_empty_matches=False)) elif round_id: - round = self.round_set.filter(id=round_id).first() + round = self.rounds.filter(id=round_id).first() if round: match_groups = self.round_match_groups(round, broadcasted, hide_empty_matches=False) else: @@ -352,11 +342,11 @@ class Tournament(BaseModel): def all_groups(self, broadcasted): groups = [] - for round in self.round_set.filter(parent=None).all().order_by('index'): + for round in self.rounds.filter(parent=None).all().order_by('index'): groups.extend(self.round_match_groups(round, broadcasted, hide_empty_matches=True)) if self.display_group_stages(): - for group_stage in self.groupstage_set.all().order_by('index'): + for group_stage in self.group_stages.all().order_by('index'): group = self.group_stage_match_group(group_stage, broadcasted, hide_empty_matches=True) if group: groups.append(group) @@ -364,7 +354,7 @@ class Tournament(BaseModel): return groups def group_stage_match_group(self, group_stage, broadcasted, hide_empty_matches): - matches = group_stage.match_set.all() + matches = group_stage.matches.all() if hide_empty_matches: matches = [m for m in matches if m.should_appear()] else: @@ -378,7 +368,7 @@ class Tournament(BaseModel): def round_match_groups(self, round, broadcasted, hide_empty_matches): groups = [] - matches = round.match_set.order_by('index').all() + matches = round.matches.order_by('index').all() if hide_empty_matches: matches = [m for m in matches if m.should_appear()] else: @@ -407,7 +397,7 @@ class Tournament(BaseModel): return MatchGroup(name, live_matches) def live_group_stages(self): - group_stages = list(self.groupstage_set.all()) + group_stages = list(self.group_stages.all()) group_stages.sort(key=lambda gs: gs.index) return [gs.live_group_stages() for gs in group_stages] @@ -445,7 +435,7 @@ class Tournament(BaseModel): matches = [] group_stages = [] - if len(self.groupstage_set.all()) > 0 and self.no_bracket_match_has_started(): + if len(self.group_stages.all()) > 0 and self.no_bracket_match_has_started(): group_stages = self.live_group_stages() matches = self.broadcasted_group_stages_matches() first_round = self.first_round() @@ -473,18 +463,18 @@ class Tournament(BaseModel): def no_bracket_match_has_started(self): matches = [] - for round in self.round_set.all(): - for match in round.match_set.all(): + for round in self.rounds.all(): + for match in round.matches.all(): if match.started(): return False return True def all_matches(self, hide_empty_matches): matches = [] - for round in self.round_set.all(): + for round in self.rounds.all(): matches.extend(round.all_matches(hide_empty_matches)) - for group_stage in self.groupstage_set.all(): - matches.extend(group_stage.match_set.all()) + for group_stage in self.group_stages.all(): + matches.extend(group_stage.matches.all()) matches = [m for m in matches if m.should_appear()] @@ -492,12 +482,12 @@ class Tournament(BaseModel): def group_stage_matches(self): matches = [] - for group_stage in self.groupstage_set.all(): - matches.extend(group_stage.match_set.all()) + for group_stage in self.group_stages.all(): + matches.extend(group_stage.matches.all()) return matches def group_stages_running(self): - if len(self.groupstage_set.all()) > 0: + if len(self.group_stages.all()) > 0: # check le debut des match de Round matches = self.group_stage_matches() running_group_stage_matches = [m for m in matches if m.end_date is None] @@ -519,7 +509,7 @@ class Tournament(BaseModel): current_round = last_started_match.round.root_round() if current_round: return current_round - main_rounds = list(self.round_set.filter(parent=None).all()) + main_rounds = list(self.rounds.filter(parent=None).all()) main_rounds.sort(key=lambda r: r.index) if main_rounds: return main_rounds[0] @@ -532,10 +522,10 @@ class Tournament(BaseModel): return matches[0] def round_for_index(self, index): - return self.round_set.filter(index=index, parent=None).first() + return self.rounds.filter(index=index, parent=None).first() def first_round(self): - main_rounds = list(self.round_set.filter(parent=None)) + main_rounds = list(self.rounds.filter(parent=None)) main_rounds.sort(key=lambda r: r.index, reverse=True) return main_rounds[0] @@ -544,13 +534,13 @@ class Tournament(BaseModel): group_stages = self.elected_broadcast_group_stages() group_stages.sort(key=lambda gs: (gs.index, gs.start_date is None, gs.start_date)) for group_stage in group_stages: - matches.extend(group_stage.match_set.all()) + matches.extend(group_stage.matches.all()) matches = [m for m in matches if m.should_appear()] matches.sort(key=lambda m: (m.start_date is None, m.end_date is not None, m.start_date, m.index)) return matches def elected_broadcast_group_stages(self): - group_stages = list(self.groupstage_set.all()) + group_stages = list(self.group_stages.all()) started = [gs for gs in group_stages if gs.starts_soon()] if len(started) > 0: return started @@ -598,7 +588,7 @@ class Tournament(BaseModel): def display_group_stages(self): if self.end_date is not None: return True - if len(self.groupstage_set.all()) == 0: + if len(self.group_stages.all()) == 0: return False if self.publish_group_stages: return True @@ -610,7 +600,7 @@ class Tournament(BaseModel): return timezone.now() >= first_group_stage_start_date def group_stage_start_date(self): - group_stages = [gs for gs in self.groupstage_set.all() if gs.start_date is not None] + group_stages = [gs for gs in self.group_stages.all() if gs.start_date is not None] if len(group_stages) == 0: return None @@ -640,7 +630,7 @@ class Tournament(BaseModel): def bracket_matches(self): matches = [] - for round in self.round_set.all(): + for round in self.rounds.all(): matches.extend(round.all_matches(False)) return matches @@ -665,7 +655,7 @@ class Tournament(BaseModel): return self.federal_level_category == FederalLevelCategory.UNLISTED def is_build_and_not_empty(self): - return (len(self.groupstage_set.all()) > 0 or len(self.round_set.all()) > 0) and len(self.teamregistration_set.all()) >= 4 + return (len(self.group_stages.all()) > 0 or len(self.rounds.all()) > 0) and len(self.team_registrations.all()) >= 4 def day_duration_formatted(self): return plural_format("jour", self.day_duration) @@ -677,7 +667,7 @@ class Tournament(BaseModel): return False def has_all_group_stages_started(self): - for group_stage in self.groupstage_set.all(): + for group_stage in self.group_stages.all(): if group_stage.has_at_least_one_started_match() == False: return False return True diff --git a/tournaments/models/views.py b/tournaments/models/views.py deleted file mode 100644 index 4114602..0000000 --- a/tournaments/models/views.py +++ /dev/null @@ -1,472 +0,0 @@ -from .serializers import ClubSerializer, CourtSerializer, DateIntervalSerializer, TournamentSerializer, UserSerializer, ChangePasswordSerializer, EventSerializer, RoundSerializer, GroupStageSerializer, MatchSerializer, TeamScoreSerializer, TeamRegistrationSerializer, PlayerRegistrationSerializer, LiveMatchSerializer, PurchaseSerializer, CustomUserSerializer, FailedApiCallSerializer, LogSerializer, DeviceTokenSerializer, DataAccessSerializer -from tournaments.models import Club, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamScore, TeamRegistration, PlayerRegistration, Court, DateInterval, Purchase, FailedApiCall, Log, DeviceToken, ModelLog, DataAccess - -from rest_framework import viewsets, permissions -from rest_framework.authtoken.models import Token -from rest_framework.response import Response -from rest_framework.decorators import api_view -from rest_framework import status -from rest_framework.generics import UpdateAPIView -from rest_framework.exceptions import MethodNotAllowed -from rest_framework.permissions import IsAuthenticated -from rest_framework.views import APIView - -from django.contrib.auth import authenticate -from django.db.models import Q -from django.core.exceptions import ObjectDoesNotExist -from django.utils import timezone -from django.apps import apps - -from collections import defaultdict - -from .permissions import IsClubOwner -from .utils import is_valid_email, build_serializer_class - -class DataApi(APIView): - permission_classes = [IsAuthenticated] - - def post(self, request, *args, **kwargs): - - # unfold content - model_operation = request.data.get('operation') - model_name = request.data.get('model_name') - data = request.data.get('data') - store_id = request.data.get('store_id') - - print(f"DataApi post > {model_operation} {model_name}") - - serializer_class = build_serializer_class(model_name) - - model = apps.get_model(app_label='tournaments', model_name=model_name) - try: - data_id = data.get('id') - instance = model.objects.get(id=data_id) - - if model_operation == 'DELETE': - parent_model, parent_id = instance.get_parent_reference() - return self.delete_and_save_log(request, data_id, model_operation, model_name, store_id, parent_id, parent_model) - else: # PUT - serializer = serializer_class(instance, data=data, context={'request': request}) - if serializer.is_valid(): - if instance.last_update <= serializer.validated_data.get('last_update'): - print('>>> update') - return self.save_and_create_log(request, serializer, model_operation, model_name, store_id) - else: - print('>>> return 203') - return Response(serializer.data, status=status.HTTP_203_NON_AUTHORITATIVE_INFORMATION) - else: - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - except model.DoesNotExist: # POST - print('>>> insert') - serializer = serializer_class(data=data, context={'request': request}) - if serializer.is_valid(): - return self.save_and_create_log(request, serializer, model_operation, model_name, store_id) - else: - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def save_and_create_log(self, request, serializer, model_operation, model_name, store_id): - instance = serializer.save() - parent_model, parent_id = instance.get_parent_reference() - - self.create_and_save_model_log( - user=request.user, - model_operation=model_operation, - model_name=model_name, - model_id=instance.id, - store_id=store_id, - parent_id=parent_id, - parent_model=parent_model - ) - - return Response(serializer.data, status=status.HTTP_201_CREATED) - - def delete_and_save_log(self, request, data_id, model_operation, model_name, store_id, parent_id, parent_model): - - model = apps.get_model(app_label='tournaments', model_name=model_name) - print(model) - try: - instance = model.objects.get(id=data_id) - instance.delete() - except model.DoesNotExist: - return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND) - - # we delete all logs linked to the instance because they won't be needed anymore - ModelLog.objects.filter(model_id=data_id).delete() - - self.create_and_save_model_log( - user=request.user, - model_operation=model_operation, - model_name=model_name, - model_id=data_id, - store_id=store_id, - parent_id=parent_id, - parent_model=parent_model - ) - - return Response(status=status.HTTP_204_NO_CONTENT) - - def create_and_save_model_log(self, user, model_operation, model_name, model_id, store_id, parent_id, parent_model): - model_log = ModelLog() - model_log.user = user - model_log.operation = model_operation - model_log.date = timezone.localtime(timezone.now()) - model_log.model_name = model_name - model_log.model_id = model_id - model_log.store_id = store_id - model_log.parent_model_id = parent_id - model_log.parent_model_name = parent_model - model_log.save() - - def get(self, request, *args, **kwargs): - print('/data GET YEAH!') - - last_update = request.query_params.get('last_update') - if not last_update: - return Response({"error": "last_update parameter is required"}, status=status.HTTP_400_BAD_REQUEST) - - print(last_update) - - try: - last_update = timezone.datetime.fromisoformat(last_update) - except ValueError: - return Response({"error": f"Invalid date format for last_update: {last_update}"}, status=status.HTTP_400_BAD_REQUEST) - - data_access = DataAccess.objects.filter(shared_with=request.user).values_list('model_id', flat=True) - log_query = Q(date__gt=last_update) & (Q(user=request.user) | Q(model_id__in=data_access)) - logs = ModelLog.objects.filter(log_query).order_by('date') - - updates = defaultdict(dict) - deletions = defaultdict(list) - - for log in logs: - model = apps.get_model(app_label='tournaments', model_name=log.model_name) - - if log.operation in ['POST', 'PUT']: - try: - instance = model.objects.get(id=log.model_id) - serializer_class = build_serializer_class(log.model_name) - serializer = serializer_class(instance) - updates[log.model_name][log.model_id] = serializer.data - except model.DoesNotExist: - # If the instance doesn't exist, it might have been deleted after this log was created - pass - elif log.operation == 'DELETE': - deletions[log.model_name].append({'model_id': log.model_id, 'store_id': log.store_id}) - - # Convert updates dict to list for each model - for model_name in updates: - updates[model_name] = list(updates[model_name].values()) - - # Convert deletions set to list for each model - for model_name in deletions: - deletions[model_name] = deletions[model_name] - - date = logs.last().date.astimezone().isoformat(timespec='seconds') if logs else None - - response_data = { - "updates": dict(updates), - "deletions": dict(deletions), - "date": date - } - - return Response(response_data, status=status.HTTP_200_OK) - -class CustomAuthToken(APIView): - permission_classes = [] - - def post(self, request, *args, **kwargs): - username = request.data.get('username') - password = request.data.get('password') - device_id = request.data.get('device_id') - - user = authenticate(username=username, password=password) - - if user is None and is_valid_email(username) == True: - true_username = self.get_username_from_email(username) - user = authenticate(username=true_username, password=password) - - if user is not None: - - # if user.device_id is None or user.device_id == device_id or user.username == 'apple-test': - # if user.device_id is None or user.device_id == device_id or user.username == 'apple-test': - user.device_id = device_id - user.save() - - token, created = Token.objects.get_or_create(user=user) - return Response({'token': token.key}) - # else: - # return Response({'error': 'Vous ne pouvez pour l\'instant vous connecter sur plusieurs appareils en même temps. Veuillez vous déconnecter du précédent appareil. Autrement, veuillez contacter le support.'}, status=status.HTTP_403_FORBIDDEN) - else: - return Response({'error': 'L\'utilisateur et le mot de passe de correspondent pas'}, status=status.HTTP_401_UNAUTHORIZED) - - def get_username_from_email(self, email): - try: - user = CustomUser.objects.get(email=email) - return user.username - except ObjectDoesNotExist: - return None # or handle the case where the user doesn't exist - -class Logout(APIView): - permission_classes = (IsAuthenticated,) - - def post(self, request, *args, **kwargs): - # request.user.auth_token.delete() - - device_id = request.data.get('device_id') - if request.user.device_id == device_id: - request.user.device_id = None - request.user.save() - - return Response(status=status.HTTP_200_OK) - -@api_view(['GET']) -def user_by_token(request): - serializer = UserSerializer(request.user) - return Response(serializer.data, status=status.HTTP_200_OK) - -class UserViewSet(viewsets.ModelViewSet): - queryset = CustomUser.objects.all() - serializer_class = CustomUserSerializer - permission_classes = [] # Users are public whereas the other requests are only for logged users - - def get_serializer_class(self): - # Use UserSerializer for other actions (e.g., create, retrieve) - if self.action in ['create', 'retrieve']: - return UserSerializer - return self.serializer_class - -class ClubViewSet(viewsets.ModelViewSet): - queryset = Club.objects.all() - serializer_class = ClubSerializer - permission_classes = [IsClubOwner] # Clubs are public whereas the other requests are only for logged users - - def perform_create(self, serializer): - serializer.save(creator=self.request.user) - -class TournamentViewSet(viewsets.ModelViewSet): - queryset = Tournament.objects.all() - serializer_class = TournamentSerializer - - def get_queryset(self): - if self.request.user.is_anonymous: - return [] - - return self.queryset.filter( - Q(event__creator=self.request.user) | - Q(id__in=DataAccess.objects.filter( - shared_with=self.request.user, - model_name=self.queryset.model.__name__ - ).values_list('model_id', flat=True)) - ) - -class PurchaseViewSet(viewsets.ModelViewSet): - queryset = Purchase.objects.all() - serializer_class = PurchaseSerializer - - def get_queryset(self): - if self.request.user.is_anonymous: - return [] - - # return self.queryset.filter(user=self.request.user) - return self.queryset.filter( - Q(user=self.request.user) | - Q(id__in=DataAccess.objects.filter( - shared_with=self.request.user, - model_name=self.queryset.model.__name__ - ).values_list('model_id', flat=True)) - ) - - def put(self, request, pk): - raise MethodNotAllowed('PUT') - - def patch(self, request, pk): - raise MethodNotAllowed('PATCH') - - def delete(self, request, pk): - raise MethodNotAllowed('DELETE') - -# class ExpandedTournamentViewSet(viewsets.ModelViewSet): -# queryset = Tournament.objects.all() -# serializer_class = ExpandedTournamentSerializer - -class ChangePasswordView(UpdateAPIView): - serializer_class = ChangePasswordSerializer - - def update(self, request, *args, **kwargs): - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - user = serializer.save() - # if using drf authtoken, create a new token - if hasattr(user, 'auth_token'): - user.auth_token.delete() - token, created = Token.objects.get_or_create(user=user) - # return new token - return Response({'token': token.key}, status=status.HTTP_200_OK) - -class EventViewSet(viewsets.ModelViewSet): - queryset = Event.objects.all() - serializer_class = EventSerializer - - def get_queryset(self): - if self.request.user.is_anonymous: - return [] - # return self.queryset.filter(creator=self.request.user) - return self.queryset.filter( - Q(creator=self.request.user) | - Q(id__in=DataAccess.objects.filter( - shared_with=self.request.user, - model_name=self.queryset.model.__name__ - ).values_list('model_id', flat=True)) - ) - -class RoundViewSet(viewsets.ModelViewSet): - queryset = Round.objects.all() - serializer_class = RoundSerializer - - def get_queryset(self): - tournament_id = self.request.query_params.get('tournament') - if tournament_id: - return self.queryset.filter(tournament=tournament_id) - if self.request.user: - return self.queryset.filter(tournament__event__creator=self.request.user) - return [] - -class GroupStageViewSet(viewsets.ModelViewSet): - queryset = GroupStage.objects.all() - serializer_class = GroupStageSerializer - - def get_queryset(self): - tournament_id = self.request.query_params.get('tournament') - if tournament_id: - return self.queryset.filter(tournament=tournament_id) - if self.request.user: - return self.queryset.filter(tournament__event__creator=self.request.user) - return [] - -class MatchViewSet(viewsets.ModelViewSet): - queryset = Match.objects.all() - serializer_class = MatchSerializer - - def get_queryset(self): - tournament_id = self.request.query_params.get('tournament') - if tournament_id: - return self.queryset.filter(Q(group_stage__tournament=tournament_id) | Q(round__tournament=tournament_id)) - if self.request.user: - return self.queryset.filter(Q(group_stage__tournament__event__creator=self.request.user) | Q(round__tournament__event__creator=self.request.user)) - return [] - -class TeamScoreViewSet(viewsets.ModelViewSet): - queryset = TeamScore.objects.all() - serializer_class = TeamScoreSerializer - - def get_queryset(self): - tournament_id = self.request.query_params.get('tournament') - if tournament_id: - return self.queryset.filter(team_registration__tournament=tournament_id) - if self.request.user: - return self.queryset.filter(team_registration__tournament__event__creator=self.request.user) - return [] - -class TeamRegistrationViewSet(viewsets.ModelViewSet): - queryset = TeamRegistration.objects.all() - serializer_class = TeamRegistrationSerializer - - def get_queryset(self): - tournament_id = self.request.query_params.get('tournament') - if tournament_id: - return self.queryset.filter(tournament=tournament_id) - if self.request.user: - return self.queryset.filter(tournament__event__creator=self.request.user) - return [] - -class PlayerRegistrationViewSet(viewsets.ModelViewSet): - queryset = PlayerRegistration.objects.all() - serializer_class = PlayerRegistrationSerializer - - def get_queryset(self): - tournament_id = self.request.query_params.get('tournament') - if tournament_id: - return self.queryset.filter(team_registration__tournament=tournament_id) - if self.request.user: - return self.queryset.filter(team_registration__tournament__event__creator=self.request.user) - return [] - -class CourtViewSet(viewsets.ModelViewSet): - queryset = Court.objects.all() - serializer_class = CourtSerializer - -class DateIntervalViewSet(viewsets.ModelViewSet): - queryset = DateInterval.objects.all() - serializer_class = DateIntervalSerializer - - def get_queryset(self): - if self.request.user.is_anonymous: - return [] - - return self.queryset.filter( - Q(event__creator=self.request.user) | - Q(event__id__in=DataAccess.objects.filter( - shared_with=self.request.user, - model_name=self.queryset.model.__name__ - ).values_list('model_id', flat=True)) - ) - -class FailedApiCallViewSet(viewsets.ModelViewSet): - queryset = FailedApiCall.objects.all() - serializer_class = FailedApiCallSerializer - permission_classes = [] # FailedApiCall are public whereas the other requests are only for logged users - - def get_queryset(self): - return [] - - def perform_create(self, serializer): - if self.request.user.is_anonymous == False: - serializer.save(user=self.request.user) - else: - serializer.save() - -class LogViewSet(viewsets.ModelViewSet): - queryset = Log.objects.all() - serializer_class = LogSerializer - permission_classes = [] # Log are public whereas the other requests are only for logged users - - def get_queryset(self): - return [] - - def perform_create(self, serializer): - if self.request.user.is_anonymous == False: - serializer.save(user=self.request.user) - else: - serializer.save() - -class DeviceTokenViewSet(viewsets.ModelViewSet): - queryset = DeviceToken.objects.all() - serializer_class = DeviceTokenSerializer - - def get_queryset(self): - if self.request.user: - return self.queryset.filter(user=self.request.user) - return [] - - def create(self, request, *args, **kwargs): - value = request.data.get('value') - if DeviceToken.objects.filter(value=value).exists(): - return Response({"detail": "This device token is already registered."}, status=208) - - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - self.perform_create(serializer) - headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) - - def perform_create(self, serializer): - serializer.save(user=self.request.user) - -class DataAccessViewSet(viewsets.ModelViewSet): - queryset = DataAccess.objects.all() - serializer_class = DataAccessSerializer - - def get_queryset(self): - if self.request.user: - return self.queryset.filter(owner=self.request.user) - return [] diff --git a/tournaments/views.py b/tournaments/views.py index 09550d1..69826dd 100644 --- a/tournaments/views.py +++ b/tournaments/views.py @@ -126,8 +126,8 @@ def tournament(request, tournament_id): group_stage_id = request.GET.get('group_stage') match_groups = tournament.match_groups(False, group_stage_id, round_id) - rounds = tournament.round_set.filter(parent=None).order_by('-index') - group_stages = tournament.groupstage_set.order_by('index') + rounds = tournament.rounds.filter(parent=None).order_by('-index') + group_stages = tournament.group_stages.order_by('index') print(len(match_groups)) print(len(rounds))