Django 信号有条件地更新一个 table 基于另一个

Django signal conditionally update one table based on another

我正在开发我的第一个 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?但是我不知道该怎么做

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

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

在您的情况下,只有 Bet 模型对 Game save/change 事件感兴趣。您可以直接访问 Game class.

我这么说是因为信号倾向于 "hide" code/business 应用程序的逻辑,使维护变得更加困难(因为你有一些代码正在执行并不是很明显)。

对我来说,这看起来像是一份常规视图的工作,您可以在其中添加游戏的分数并 "close" 它。可能是一个额外的字段(可能是 BooleanFieldDateTimeField)以指示 Game 结束。

看下面的例子:

forms.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() 方法,如下所示:

models.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 循环。

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