Skip to main content
 首页 » 编程设计

python之Django 信号有条件地根据另一个表更新一个表

2024年09月07日31mengfanrong

我正在开发我的第一个 django 项目,它是一个体育博彩游戏。

这是我的模型:

class Game(models.Model): 
    home_team = models.CharField(max_length=200) 
    away_team = models.CharField(max_length=200) 
    home_goals = models.IntegerField(default=None) 
    away_goals = models.IntegerField(default=None) 
 
 
class Bet(models.Model): 
    gameid = models.ForeignKey(Game, on_delete=models.CASCADE) 
    userid = models.ForeignKey(User, on_delete=models.CASCADE) 
    home_goals = models.IntegerField() 
    away_goals = models.IntegerField() 
    score = models.IntegerField(default=None, null=True) 

首先,我创建了一个在目标字段中包含空值的游戏实例,然后用户下注。 游戏结束后,我会更新游戏目标字段。现在我需要像这样为每个用户分配积分:

WHEN bet.home_goals = game.home_goals AND bet.away_goals = game.away_goals THEN 2 
WHEN game.home_goals > game.away_goals AND bet.home_goals > bet.away_goals THEN 1  
WHEN game.home_goals < game.away_goals AND bet.home_goals < bet.away_goals THEN 1  
WHEN bet.home_goals = bet.away_goals AND game.home_goals = game.away_goals THEN 1  
ELSE 0 

我似乎应该创建一个 POST_SAVE 信号来根据 Game.home_goals 和 Game.away_goals 的更新为每个用户更新 Bet.score?但是我不知道该怎么做

请您参考如下方法:

我建议远离信号。一般来说,您应该在以下情况下使用信号:

  • 多段代码对同一个事件感兴趣;
  • 您需要与第三方代码(您无法直接访问)进行交互。

在您的例子中,只有 Bet 模型对 Game 保存/更改事件感兴趣。您可以直接访问 Game 类。

我这么说是因为信号往往会“隐藏”您应用程序的代码/业务逻辑,从而使维护变得更加困难(因为您无法立即明显看出您正在执行某些代码)。

对我来说,这看起来像是常规 View 的工作,您可以在其中添加游戏得分并“关闭”它。可能是一个额外的字段(可以是 BooleanField 或 DateTimeField)来指示 Game 已经结束。

请看下面的例子:

表单.py

from .models import Game 
from django import forms 
from django.db import transaction 
 
class GameForm(forms.ModelForm): 
    class Meta: 
        model = Game 
        fields = ('home_goals', 'away_goals') 
 
    # do everything inside the same database transaction to make sure the data is consistent 
    @transaction.atomic 
    def save(self):             
        game = super().save(commit=True) 
        for bet in game.bet_set.all(): 
            if bet.home_goals == game.home_goals and bet.away_goals == game.away_goals: 
                bet.score = 2 
            elif <build_your_logic_here>: 
                bet.score = 1 
            else: 
                bet.score = 0 
            bet.save() 
        return game 

views.py

from django.shortcuts import redirect 
from .forms import GameForm 
 
def end_game(request, game_id): 
    game = Game.objects.get(pk=game_id) 
    if request.method == 'POST': 
        form = GameForm(request.POST, instance=game) 
        if form.is_valid(): 
            form.save() 
            return redirect('/gameboard/')  # add here the relevant url where to send the user 
    else: 
        form = GameForm(instance=game) 
 
    return render(request, 'game_form.html', {'form': form}) 

如果由于某种原因,分数更改事件发生在多个点(即,模型由应用程序的不同部分更新),在您的情况下,最好的选择是覆盖 save() 方法,像这样:

模型.py

class Game(models.Model): 
    home_team = models.CharField(max_length=200) 
    away_team = models.CharField(max_length=200) 
    home_goals = models.IntegerField(default=None) 
    away_goals = models.IntegerField(default=None) 
 
    def save(self, *args, **kwargs): 
        # call the save method 
        super().save(*args, **kwargs) 
 
        # execute your extra logic  
        for bet in self.bet_set.all(): 
            if bet.home_goals == self.home_goals and bet.away_goals == self.away_goals: 
                bet.score = 2 
            # rest of the if/else logic 
            bet.save() 

这将是与信号类似的实现,但我会说得更明确。正如我所提到的,我认为这不是解决您的问题的最佳方案。这可能会降低您的应用程序的速度,因为每次您保存 Game 实例时都会执行此 for 循环。

但是,如果您想了解有关信号的更多信息,我写了一篇关于它的博文:How to Create Django Signals .