在复杂的结构化 Django 模型中使用聚合的正确方法是什么

问题描述 投票:0回答:1

我有一个代表足球联赛的 Django 应用程序,应该显示得分和其他内容,我需要根据此应用程序中的模型创建一个函数来获取当前进球数、得分、获胜比赛和位置的总和季节,这是我的模特:

模型.py

class TeamName(models.Model):
    """
    Stores Available team name to be picked later by users
    """

    name = models.CharField(max_length=33, verbose_name=_(
        "Team Name"), help_text=_("Name of the team to be used by players"))
    logo = models.FileField(upload_to="uploads", verbose_name=_(
        "Logo Image"), help_text=_("The File that contains the team logo image"), null=True)

    def image_tag(self):
        """
        This method created a thumbnil of the image to be viewed at
        the listing of logo objects :model:'teams.models.TeamName'
        """
        return mark_safe(f'<img src="/uploads/{self.logo}" width="100" height="100" />')

    image_tag.short_description = _("Logo")
    image_tag.allow_tags = True

    class Meta:
        """
        Defines the name of the model that will be viewied by users
        """
        verbose_name = _("1. Team Name")

    def __str__(self) -> str:
        """
        Make sure to view the name as string not the id or pk
        """
        return str(self.name)


class TeamStrip(models.Model):
    """
    Stores Available team Strip to be picked later by users
    """
    image = models.FileField(upload_to="uploads/uploads", verbose_name=_(
        "Team Strips"), help_text=_("A Strip to be used later by users"))

    def image_tag(self):
        """
        This method created a thumbnil of the image to be viewed
        at the listing of logo objects :model:'teams.models.TeamLogo'
        """
        return mark_safe(f'<img src="/uploads/{self.image}" width="100" height="100" />')

    image_tag.short_description = 'Image'
    image_tag.allow_tags = True

    class Meta:
        """
        Defines the name of the model that will be viewied by users
        """
        verbose_name = _("2. Team Strip")


class Team(models.Model):
    """
    Stores Available teams 
    """
    name = models.ForeignKey(TeamName, on_delete=models.CASCADE, related_name="team_set_for_name",
                             verbose_name=_("Team Name"), help_text="Name of the team")
    home_strip = models.ForeignKey(TeamStrip, on_delete=models.CASCADE, related_name="team_set_for_home_teamstrip",
                                   verbose_name=_(
                                       "Team Home Strip"), help_text="Home Shirt for the team")
    away_strip = models.ForeignKey(TeamStrip, on_delete=models.CASCADE, related_name="team_set_for_away_teamstrip",
                                   verbose_name=_(
                                       "Team Away Strip"), help_text="Away Shirt for the team")
    league = models.ForeignKey("leagues.LeagueSeason", on_delete=models.CASCADE, related_name="team_set_for_league",
                               null=True, verbose_name=_("League Season"),
                               help_text=_("League season that team plays in "))
    cap = models.ForeignKey("players.PlayerProfile", on_delete=models.CASCADE,
                            related_name="team_set_for_cap_playerprofile",
                            verbose_name=_("Team Captain"), help_text=_("Captain of the team"))
    players = models.ManyToManyField("players.PlayerProfile", blank=True, verbose_name=_(
        "Team Players"), help_text=_("Players that is playing in the team"), related_name="team_set_for_players")
    average_skill = models.DecimalField(max_digits=5, decimal_places=2,
                                        default=0, verbose_name=_(
            "Average Team Skill"), help_text=_("An Average of Player's skills"))
    points = models.PositiveIntegerField(default=0, verbose_name=_("Team Points"),
                                         help_text=_("Team points in the current league season"))

    def logo_tag(self):
        """
        This method created a thumbnil of the image to be viewed at
        the listing of logo objects :model:'teams.models.TeamName'
        """
        return mark_safe(f'<img src="/uploads/{self.name.logo}" width="100" height="100" />')

    logo_tag.short_description = _("Logo")
    logo_tag.allow_tags = True

    def home_strip_tag(self):
        """
        This method created a thumbnail of the image to be viewed at
        """
        return mark_safe('<img src="/uploads/%s" width="50" height="50" />' % (self.home_strip.image))

    home_strip_tag.short_description = _("Home Strip")

    def away_strip_tag(self):
        """
        This method created a thumbnail of the image to be viewed at
        """
        return mark_safe('<img src="/uploads/%s" width="50" height="50" />' % (self.away_strip.image))

    away_strip_tag.short_description = _("Away Strip")

    class Meta:
        """
        Defines the name of the model that will be viewed by users
        Defines the ordering of queryset
        """
        verbose_name = _("3. Team")
        ordering = ["-points"]

    def __str__(self) -> str:
        """
        Make sure to view the name as string not the id or pk
        """
        return mark_safe(
            f"{self.name.name} {self.league.region.name_ar} {self.league.region.name_en}")  # pylint: disable=maybe-no-member

    def save(self, *args, **kwargs):
        """
        This method removes the saved  :model:'teams.models.TeamStrip' - :model:'teams.models.TeamLogo' - :model:'teams.models.TeamName' from the Regions :model:locations.models.Region
        """
        if not self.pk:
            self.league.available_team_names.remove(
                self.name)
        super(Team, self).save(*args, **kwargs)

    @staticmethod
    def autocomplete_search_fields():
        """
        This method used to define what fields to be searched by user in admin dashboard
        """
        return ("id__iexact", "name__name__icontains", "league__region__name_ar__icontains",
                "league__region__name_en__icontains",)


class JoinRequest(models.Model):
    """
    Store available Join Requests 
    """
    status_choices = (
        ('pending', 'pending'),
        ('accepted', 'accepted'),
        ('refused', 'refused'),
    )
    created = models.DateTimeField(auto_now_add=True, verbose_name=_(
        "Created on"), help_text=_("Data the request created at"))
    team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="joinrequest_set_for_team",
                             verbose_name=_("Team"), help_text=_("Team the request to join sent to"))
    player = models.ForeignKey("players.PlayerProfile", on_delete=models.CASCADE,
                               related_name="joinrequest_set_for_playerprofile", verbose_name=_(
            "Player"), help_text=_("Player that sent the request"))
    status = models.CharField(choices=status_choices, max_length=33, default='pending', verbose_name=_(
        "Status"), help_text=_("Status of the request"))

    class Meta:
        """
        Defines the name of the model that will be viewied by users
        """
        verbose_name = _("4. Join Requests")

class LeagueSeason(models.Model):
    """
    Saves League seasson in the current region

    relations
    -----------
    :models:`locations.models.Region`
    :models:`players.models.PlayerProfile`
    :models:`teams.models.Team`
    :models:`teams.models.TeamName`
    :models:`teams.models.TeamLogo`
    :models:`teams.models.TeamStrip`

    """
    status_choices = (
        ('upcoming', 'upcoming'),
        ('current', 'current'),
        ('finished', 'finished'),
    )
    start_date = models.DateField(verbose_name=_(
        "Start Date"), help_text=_("The date when seasson start"))
    is_accepting = models.BooleanField(default=True, verbose_name=_(
        "Is Accepting Teams"), help_text=_("True if the league still accepts teams"))
    region = models.ForeignKey(Region, on_delete=models.PROTECT, verbose_name=_(
        "Region"), help_text=_("The Region of the League seasson"))
    players = models.ManyToManyField(PlayerProfile, verbose_name=_(
        "Players"), help_text=_("PLayers in this league"))
    teams = models.ManyToManyField('teams.Team', blank=True, verbose_name=_(
        "Teams"), help_text=_("Teams in this League seasson"))
    status = models.CharField(choices=status_choices, max_length=33, default='upcoming', verbose_name=_(
        "Status"), help_text=_("Current Status of the league seasson"))
    available_team_names = models.ManyToManyField(TeamName, verbose_name=_(
        "Available Team Names"), help_text=_("Pickable Team Names in this seasson"))
    available_team_strips = models.ManyToManyField(TeamStrip, verbose_name=_(
        "Available Team Strips"), help_text=_("Pickable Team Strips in this seasson"))

    class Meta:
        """
        Make sure to change the appearane name of 
        the model :model:`league.models.LeagueSeasson` to be 
        Seasson
        """
        verbose_name = _("Seasson")

    def __str__(self) -> str:
        """
        Change the name that user see in the lists of :model:`leagues.models.LeagueSeasson` 
        to be Region name then start date
        """
        return f"{self.region} - {self.start_date}"


class Match(models.Model):
    """
    Saves matches data

    Relations
    ----------
    :model:`teams.models.Team`
    :model:`leagues.models.LeagueSeasson`
    :model:`location.models.Location`
    """
    date_time = models.DateTimeField(verbose_name=_(
        "Date and time"), help_text=_("Date and time of the match"))
    home_team = models.ForeignKey('teams.Team', related_name="home_team_team", on_delete=models.PROTECT, verbose_name=_(
        "Home Team"), help_text=_("Home Side team in the match"))
    away_team = models.ForeignKey('teams.Team', related_name="away_team_team", on_delete=models.PROTECT, verbose_name=_(
        "Away Team"), help_text=_("Away Side team in the match"))
    league = models.ForeignKey(LeagueSeason, on_delete=models.CASCADE, null=True, verbose_name=_(
        "League Seasson"), help_text=_("The Seasson of this match"))
    location = models.ForeignKey('locations.Location', on_delete=models.PROTECT, null=True, verbose_name=_(
        "Location"), help_text=_("Location where the match will be played"))

    class Meta:
        """
        Changes the Appearance name of 
        :model:`leagues.models.Match` to be Match
        """
        verbose_name = _("Match")


class Goal(models.Model):
    """
    Saves goal records in every match
    related to :model:`players.models.PlayerProfile` and :model:`teams.models.Team` .
    """
    match = models.ForeignKey(Match, on_delete=models.CASCADE, verbose_name=_(
        "Match"), help_text=_("Match where the goal was scored"))
    team = models.ForeignKey("teams.Team", on_delete=models.PROTECT, null=True,
                             blank=True, verbose_name=_("Team"), help_text=_("The team scored the goal"))
    player = models.ForeignKey('players.PlayerProfile', related_name="goal_maker", on_delete=models.PROTECT,
                               null=True, blank=True, verbose_name=_("Player"), help_text=_("Player who scored the goal"))
    assistant = models.ForeignKey('players.PlayerProfile', related_name="goal_assist", on_delete=models.PROTECT,
                                  null=True, blank=True, verbose_name=_("Assist"), help_text=_("PLayer who assisted scoring this goal"))

    class Meta:
        """
        Make sure to see the model  :model:`leagues.models.Goal` name 
        as Goal for user
        """
        verbose_name = _("Goal")

    def __str__(self) -> str:
        """
        Show the object name as the name of the player 
        who scored the goal from :model:`users.models.AppUser` .
        """
        return f"{self.player.app_user.first_name} {self.player.app_user.last_name}"


class Card(models.Model):
    """
    Saves Cards records in every match
    related to :model:`players.models.PlayerProfile` , :model:`leagues.Match`and :model:`teams.models.Team` .
    """
    CARDS_ENUM = (
        (_("Red Card"), _("Red Card")),
        (_("Yellow Card"), _("Yellow Card")),
    )
    match = models.ForeignKey(Match, on_delete=models.CASCADE, related_name="card_set_for_match", verbose_name=_(
        "Match"), help_text=_("Match where the goal was scored"))
    team = models.ForeignKey("teams.Team", on_delete=models.PROTECT, null=True, blank=True,
                             related_name="card_set_for_team", verbose_name=_("Team"), help_text=_("The team scored the goal"))
    player = models.ForeignKey('players.PlayerProfile', on_delete=models.PROTECT, null=True, blank=True,
                               related_name="card_set_for_playerprofile", verbose_name=_("Player"), help_text=_("Player who scored the goal"))
    type = models.CharField(max_length=100, choices=CARDS_ENUM, verbose_name=_(
        "Card Type"), help_text=_("Type of the card "))

    class Meta:
        """
        Make sure to see the model  :model:`leagues.models.Card` name 
        as Card for user
        """
        verbose_name = _("Card")

    def __str__(self) -> str:
        """
        Show the object name as the name of the player 
        who scored the goal from :model:`users.models.AppUser` .
        """
        return f"{self.player.app_user.first_name} {self.player.app_user.last_name} - {self.type}"

这是我尝试实现结果的函数:

views.py

@permission_classes(["IsAuthenticated"])
@api_view(["POST"])
def get_league_scores(request):
    league_id = request.data["league_id"]

    try:
        season = LeagueSeason.objects.get(pk=league_id)
    except LeagueSeason.DoesNotExist:
        return Response({"error": "LeagueSeason not found"}, status=404)

    teams = Team.objects.filter(league=season)

    team_stats = []

    for team in teams:
        # Calculate team statistics
        matches_played = team.home_team_team.filter(league=season).count() + team.away_team_team.filter(league=season).count()
        matches_won = team.home_team_team.filter(league=season, home_team=team, home_team_goals__gt=F('away_team_goals')).count() + team.away_team_team.filter(league=season, away_team=team, away_team_goals__gt=F('home_team_goals')).count()
        matches_lost = team.home_team_team.filter(league=season, home_team=team, home_team_goals__lt=F('away_team_goals')).count() + team.away_team_team.filter(league=season, away_team=team, away_team_goals__lt=F('home_team_goals')).count()
        matches_tied = matches_played - (matches_won + matches_lost)
        goals_scored = team.home_team_team.filter(league=season).aggregate(Sum('home_team_goals'))['home_team_goals__sum'] + team.away_team_team.filter(league=season).aggregate(Sum('away_team_goals'))['away_team_goals__sum']
        points = (matches_won * 3) + matches_tied


        team_stats.append(
            {
                "team_name": team.name.name,
                "matches_played": matches_played,
                "matches_won": matches_won,
                "matches_lost": matches_lost,
                "matches_tied": matches_tied,
                "goals_scored": goals_scored,
                "points": points,
            }
        )

    # Sort the teams based on points, from lowest to highest
    sorted_team_stats = sorted(team_stats, key=lambda x: x["points"], reverse=True)

    # Add position to each team based on the sorted order
    for i, team_stat in enumerate(sorted_team_stats, start=1):
        team_stat["position"] = i

    return Response(sorted_team_stats, status=200)

当我尝试调用此 API 时收到此错误

Cannot resolve keyword 'home_team_goals' into field. Choices are: away_team, away_team_id, card_set_for_match, date_time, goal, home_team, home_team_id, id, league, league_id, location, location_id

python django aggregate
1个回答
0
投票

view.py中这行代码

season = LeagueSeason.objects.get(league_id)

我认为必须是:

season = LeagueSeason.objects.get(pk=league_id)

这一行被输入了两次

league_id = request.data.get('league_id')

模型.py 我不明白联赛赛季中的关系。 模型中似乎遗漏了一些东西。

© www.soinside.com 2019 - 2024. All rights reserved.