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" 它。可能是一个额外的字段(可能是 BooleanField
或 DateTimeField
)以指示 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。
我正在开发我的第一个 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" 它。可能是一个额外的字段(可能是 BooleanField
或 DateTimeField
)以指示 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。