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

flmtquvp  于 2023-11-20  发布在  Go
关注(0)|答案(2)|浏览(162)

我有一个Django应用程序,它代表一个足球联赛,应该显示得分点和其他东西,我需要根据这个应用程序中的模型创建一个函数,以获得当前赛季的目标,积分,赢得的比赛和位置的总和,这里是我的模型:

models.py

  1. class TeamName(models.Model):
  2. """
  3. Stores Available team name to be picked later by users
  4. """
  5. name = models.CharField(max_length=33, verbose_name=_(
  6. "Team Name"), help_text=_("Name of the team to be used by players"))
  7. logo = models.FileField(upload_to="uploads", verbose_name=_(
  8. "Logo Image"), help_text=_("The File that contains the team logo image"), null=True)
  9. def image_tag(self):
  10. """
  11. This method created a thumbnil of the image to be viewed at
  12. the listing of logo objects :model:'teams.models.TeamName'
  13. """
  14. return mark_safe(f'<img src="/uploads/{self.logo}" width="100" height="100" />')
  15. image_tag.short_description = _("Logo")
  16. image_tag.allow_tags = True
  17. class Meta:
  18. """
  19. Defines the name of the model that will be viewied by users
  20. """
  21. verbose_name = _("1. Team Name")
  22. def __str__(self) -> str:
  23. """
  24. Make sure to view the name as string not the id or pk
  25. """
  26. return str(self.name)
  27. class TeamStrip(models.Model):
  28. """
  29. Stores Available team Strip to be picked later by users
  30. """
  31. image = models.FileField(upload_to="uploads/uploads", verbose_name=_(
  32. "Team Strips"), help_text=_("A Strip to be used later by users"))
  33. def image_tag(self):
  34. """
  35. This method created a thumbnil of the image to be viewed
  36. at the listing of logo objects :model:'teams.models.TeamLogo'
  37. """
  38. return mark_safe(f'<img src="/uploads/{self.image}" width="100" height="100" />')
  39. image_tag.short_description = 'Image'
  40. image_tag.allow_tags = True
  41. class Meta:
  42. """
  43. Defines the name of the model that will be viewied by users
  44. """
  45. verbose_name = _("2. Team Strip")
  46. class Team(models.Model):
  47. """
  48. Stores Available teams
  49. """
  50. name = models.ForeignKey(TeamName, on_delete=models.CASCADE, related_name="team_set_for_name",
  51. verbose_name=_("Team Name"), help_text="Name of the team")
  52. home_strip = models.ForeignKey(TeamStrip, on_delete=models.CASCADE, related_name="team_set_for_home_teamstrip",
  53. verbose_name=_(
  54. "Team Home Strip"), help_text="Home Shirt for the team")
  55. away_strip = models.ForeignKey(TeamStrip, on_delete=models.CASCADE, related_name="team_set_for_away_teamstrip",
  56. verbose_name=_(
  57. "Team Away Strip"), help_text="Away Shirt for the team")
  58. league = models.ForeignKey("leagues.LeagueSeason", on_delete=models.CASCADE, related_name="team_set_for_league",
  59. null=True, verbose_name=_("League Season"),
  60. help_text=_("League season that team plays in "))
  61. cap = models.ForeignKey("players.PlayerProfile", on_delete=models.CASCADE,
  62. related_name="team_set_for_cap_playerprofile",
  63. verbose_name=_("Team Captain"), help_text=_("Captain of the team"))
  64. players = models.ManyToManyField("players.PlayerProfile", blank=True, verbose_name=_(
  65. "Team Players"), help_text=_("Players that is playing in the team"), related_name="team_set_for_players")
  66. average_skill = models.DecimalField(max_digits=5, decimal_places=2,
  67. default=0, verbose_name=_(
  68. "Average Team Skill"), help_text=_("An Average of Player's skills"))
  69. points = models.PositiveIntegerField(default=0, verbose_name=_("Team Points"),
  70. help_text=_("Team points in the current league season"))
  71. def logo_tag(self):
  72. """
  73. This method created a thumbnil of the image to be viewed at
  74. the listing of logo objects :model:'teams.models.TeamName'
  75. """
  76. return mark_safe(f'<img src="/uploads/{self.name.logo}" width="100" height="100" />')
  77. logo_tag.short_description = _("Logo")
  78. logo_tag.allow_tags = True
  79. def home_strip_tag(self):
  80. """
  81. This method created a thumbnail of the image to be viewed at
  82. """
  83. return mark_safe('<img src="/uploads/%s" width="50" height="50" />' % (self.home_strip.image))
  84. home_strip_tag.short_description = _("Home Strip")
  85. def away_strip_tag(self):
  86. """
  87. This method created a thumbnail of the image to be viewed at
  88. """
  89. return mark_safe('<img src="/uploads/%s" width="50" height="50" />' % (self.away_strip.image))
  90. away_strip_tag.short_description = _("Away Strip")
  91. class Meta:
  92. """
  93. Defines the name of the model that will be viewed by users
  94. Defines the ordering of queryset
  95. """
  96. verbose_name = _("3. Team")
  97. ordering = ["-points"]
  98. def __str__(self) -> str:
  99. """
  100. Make sure to view the name as string not the id or pk
  101. """
  102. return mark_safe(
  103. f"{self.name.name} {self.league.region.name_ar} {self.league.region.name_en}") # pylint: disable=maybe-no-member
  104. def save(self, *args, **kwargs):
  105. """
  106. This method removes the saved :model:'teams.models.TeamStrip' - :model:'teams.models.TeamLogo' - :model:'teams.models.TeamName' from the Regions :model:locations.models.Region
  107. """
  108. if not self.pk:
  109. self.league.available_team_names.remove(
  110. self.name)
  111. super(Team, self).save(*args, **kwargs)
  112. @staticmethod
  113. def autocomplete_search_fields():
  114. """
  115. This method used to define what fields to be searched by user in admin dashboard
  116. """
  117. return ("id__iexact", "name__name__icontains", "league__region__name_ar__icontains",
  118. "league__region__name_en__icontains",)
  119. class JoinRequest(models.Model):
  120. """
  121. Store available Join Requests
  122. """
  123. status_choices = (
  124. ('pending', 'pending'),
  125. ('accepted', 'accepted'),
  126. ('refused', 'refused'),
  127. )
  128. created = models.DateTimeField(auto_now_add=True, verbose_name=_(
  129. "Created on"), help_text=_("Data the request created at"))
  130. team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="joinrequest_set_for_team",
  131. verbose_name=_("Team"), help_text=_("Team the request to join sent to"))
  132. player = models.ForeignKey("players.PlayerProfile", on_delete=models.CASCADE,
  133. related_name="joinrequest_set_for_playerprofile", verbose_name=_(
  134. "Player"), help_text=_("Player that sent the request"))
  135. status = models.CharField(choices=status_choices, max_length=33, default='pending', verbose_name=_(
  136. "Status"), help_text=_("Status of the request"))
  137. class Meta:
  138. """
  139. Defines the name of the model that will be viewied by users
  140. """
  141. verbose_name = _("4. Join Requests")
  142. class LeagueSeason(models.Model):
  143. """
  144. Saves League seasson in the current region
  145. relations
  146. -----------
  147. :models:`locations.models.Region`
  148. :models:`players.models.PlayerProfile`
  149. :models:`teams.models.Team`
  150. :models:`teams.models.TeamName`
  151. :models:`teams.models.TeamLogo`
  152. :models:`teams.models.TeamStrip`
  153. """
  154. status_choices = (
  155. ('upcoming', 'upcoming'),
  156. ('current', 'current'),
  157. ('finished', 'finished'),
  158. )
  159. start_date = models.DateField(verbose_name=_(
  160. "Start Date"), help_text=_("The date when seasson start"))
  161. is_accepting = models.BooleanField(default=True, verbose_name=_(
  162. "Is Accepting Teams"), help_text=_("True if the league still accepts teams"))
  163. region = models.ForeignKey(Region, on_delete=models.PROTECT, verbose_name=_(
  164. "Region"), help_text=_("The Region of the League seasson"))
  165. players = models.ManyToManyField(PlayerProfile, verbose_name=_(
  166. "Players"), help_text=_("PLayers in this league"))
  167. teams = models.ManyToManyField('teams.Team', blank=True, verbose_name=_(
  168. "Teams"), help_text=_("Teams in this League seasson"))
  169. status = models.CharField(choices=status_choices, max_length=33, default='upcoming', verbose_name=_(
  170. "Status"), help_text=_("Current Status of the league seasson"))
  171. available_team_names = models.ManyToManyField(TeamName, verbose_name=_(
  172. "Available Team Names"), help_text=_("Pickable Team Names in this seasson"))
  173. available_team_strips = models.ManyToManyField(TeamStrip, verbose_name=_(
  174. "Available Team Strips"), help_text=_("Pickable Team Strips in this seasson"))
  175. class Meta:
  176. """
  177. Make sure to change the appearane name of
  178. the model :model:`league.models.LeagueSeasson` to be
  179. Seasson
  180. """
  181. verbose_name = _("Seasson")
  182. def __str__(self) -> str:
  183. """
  184. Change the name that user see in the lists of :model:`leagues.models.LeagueSeasson`
  185. to be Region name then start date
  186. """
  187. return f"{self.region} - {self.start_date}"
  188. class Match(models.Model):
  189. """
  190. Saves matches data
  191. Relations
  192. ----------
  193. :model:`teams.models.Team`
  194. :model:`leagues.models.LeagueSeasson`
  195. :model:`location.models.Location`
  196. """
  197. date_time = models.DateTimeField(verbose_name=_(
  198. "Date and time"), help_text=_("Date and time of the match"))
  199. home_team = models.ForeignKey('teams.Team', related_name="home_team_team", on_delete=models.PROTECT, verbose_name=_(
  200. "Home Team"), help_text=_("Home Side team in the match"))
  201. away_team = models.ForeignKey('teams.Team', related_name="away_team_team", on_delete=models.PROTECT, verbose_name=_(
  202. "Away Team"), help_text=_("Away Side team in the match"))
  203. league = models.ForeignKey(LeagueSeason, on_delete=models.CASCADE, null=True, verbose_name=_(
  204. "League Seasson"), help_text=_("The Seasson of this match"))
  205. location = models.ForeignKey('locations.Location', on_delete=models.PROTECT, null=True, verbose_name=_(
  206. "Location"), help_text=_("Location where the match will be played"))
  207. class Meta:
  208. """
  209. Changes the Appearance name of
  210. :model:`leagues.models.Match` to be Match
  211. """
  212. verbose_name = _("Match")
  213. class Goal(models.Model):
  214. """
  215. Saves goal records in every match
  216. related to :model:`players.models.PlayerProfile` and :model:`teams.models.Team` .
  217. """
  218. match = models.ForeignKey(Match, on_delete=models.CASCADE, verbose_name=_(
  219. "Match"), help_text=_("Match where the goal was scored"))
  220. team = models.ForeignKey("teams.Team", on_delete=models.PROTECT, null=True,
  221. blank=True, verbose_name=_("Team"), help_text=_("The team scored the goal"))
  222. player = models.ForeignKey('players.PlayerProfile', related_name="goal_maker", on_delete=models.PROTECT,
  223. null=True, blank=True, verbose_name=_("Player"), help_text=_("Player who scored the goal"))
  224. assistant = models.ForeignKey('players.PlayerProfile', related_name="goal_assist", on_delete=models.PROTECT,
  225. null=True, blank=True, verbose_name=_("Assist"), help_text=_("PLayer who assisted scoring this goal"))
  226. class Meta:
  227. """
  228. Make sure to see the model :model:`leagues.models.Goal` name
  229. as Goal for user
  230. """
  231. verbose_name = _("Goal")
  232. def __str__(self) -> str:
  233. """
  234. Show the object name as the name of the player
  235. who scored the goal from :model:`users.models.AppUser` .
  236. """
  237. return f"{self.player.app_user.first_name} {self.player.app_user.last_name}"
  238. class Card(models.Model):
  239. """
  240. Saves Cards records in every match
  241. related to :model:`players.models.PlayerProfile` , :model:`leagues.Match`and :model:`teams.models.Team` .
  242. """
  243. CARDS_ENUM = (
  244. (_("Red Card"), _("Red Card")),
  245. (_("Yellow Card"), _("Yellow Card")),
  246. )
  247. match = models.ForeignKey(Match, on_delete=models.CASCADE, related_name="card_set_for_match", verbose_name=_(
  248. "Match"), help_text=_("Match where the goal was scored"))
  249. team = models.ForeignKey("teams.Team", on_delete=models.PROTECT, null=True, blank=True,
  250. related_name="card_set_for_team", verbose_name=_("Team"), help_text=_("The team scored the goal"))
  251. player = models.ForeignKey('players.PlayerProfile', on_delete=models.PROTECT, null=True, blank=True,
  252. related_name="card_set_for_playerprofile", verbose_name=_("Player"), help_text=_("Player who scored the goal"))
  253. type = models.CharField(max_length=100, choices=CARDS_ENUM, verbose_name=_(
  254. "Card Type"), help_text=_("Type of the card "))
  255. class Meta:
  256. """
  257. Make sure to see the model :model:`leagues.models.Card` name
  258. as Card for user
  259. """
  260. verbose_name = _("Card")
  261. def __str__(self) -> str:
  262. """
  263. Show the object name as the name of the player
  264. who scored the goal from :model:`users.models.AppUser` .
  265. """
  266. return f"{self.player.app_user.first_name} {self.player.app_user.last_name} - {self.type}"

字符串
下面是我尝试实现的结果:

views.py

  1. @permission_classes(["IsAuthenticated"])
  2. @api_view(["POST"])
  3. def get_league_scores(request):
  4. league_id = request.data["league_id"]
  5. try:
  6. season = LeagueSeason.objects.get(pk=league_id)
  7. except LeagueSeason.DoesNotExist:
  8. return Response({"error": "LeagueSeason not found"}, status=404)
  9. teams = Team.objects.filter(league=season)
  10. team_stats = []
  11. for team in teams:
  12. # Calculate team statistics
  13. matches_played = team.home_team_team.filter(league=season).count() + team.away_team_team.filter(league=season).count()
  14. 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()
  15. 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()
  16. matches_tied = matches_played - (matches_won + matches_lost)
  17. 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']
  18. points = (matches_won * 3) + matches_tied
  19. team_stats.append(
  20. {
  21. "team_name": team.name.name,
  22. "matches_played": matches_played,
  23. "matches_won": matches_won,
  24. "matches_lost": matches_lost,
  25. "matches_tied": matches_tied,
  26. "goals_scored": goals_scored,
  27. "points": points,
  28. }
  29. )
  30. # Sort the teams based on points, from lowest to highest
  31. sorted_team_stats = sorted(team_stats, key=lambda x: x["points"], reverse=True)
  32. # Add position to each team based on the sorted order
  33. for i, team_stat in enumerate(sorted_team_stats, start=1):
  34. team_stat["position"] = i
  35. return Response(sorted_team_stats, status=200)


当我尝试调用这个API时,我得到这个错误

  1. 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

nwsw7zdq

nwsw7zdq1#

view.py中的此行代码

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

字符串
我认为应该是:

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


这一行输入两次

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


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

展开查看全部
3bygqnnd

3bygqnnd2#

我将通过向Match表添加一个赢家字段来对数据库进行一点非规范化:

  1. class Match(models.Model):
  2. # Null if match was a draw.
  3. winner = models.ForeignKey("teams.Team", null=True, blank=True, on_delete=models.SET_NULL)

字符串
您可以在保存模型示例或使用数据库触发器时填充该字段。
然后,查询简化为:

  1. matches = Match.objects.filter(Q(home_team=team) | Q(away_team=team), league=season)
  2. matches_played = matches.count()
  3. matches_won = matches.filter(winner=team).count()
  4. matches_tied = matches.filter(winner__isnull=True).count()
  5. matches_lost = matches_played - (matches_won + matches_tied)
  6. goals_scored = Goal.objects.filter(team=team, match__league=season).count()

展开查看全部

相关问题