Тестирование django завершается ошибкой DoesNotExist, хотя на самом деле оно работает
Я пытаюсь проверить, может ли моя модель PlayerPoint
дать мне 5 лучших игроков по их очкам.
Вот модель Player
:
class Player(AbstractUser):
phone_number = models.CharField(
max_length=14,
unique=True,
help_text="Please ensure +251 is included"
)
first_name = models.CharField(
max_length=40,
help_text="please ensure that you've spelt it correctly"
)
father_name = models.CharField(
max_length=40,
help_text="please ensure that you've spelt it correctly"
)
grandfather_name = models.CharField(
max_length=40,
help_text="please ensure that you've spelt it correctly"
)
email = models.EmailField(
unique=True,
help_text="please ensure that the email is correct"
)
age = models.CharField(max_length=3)
profile_pic = models.ImageField(upload_to='profile_pix', default='profile_pix/placeholder.jpg')
joined_ts = models.DateTimeField(auto_now_add=True, null=False)
username = None
и это PlayerPoint
модель:
class PlayerPoint(models.Model):
OPERATION_CHOICES = (('ADD', 'ADD'), ('SUB', 'SUBTRACT'), ('RMN', 'REMAIN'))
points = models.IntegerField(null=False, default=0)
operation = models.CharField(
max_length=3,
null=False,
choices=OPERATION_CHOICES,
default=OPERATION_CHOICES[2][0]
)
operation_amount = models.IntegerField(null=False)
operation_reason = models.CharField(null=False, max_length=1500)
player = models.ForeignKey(
settings.AUTH_USER_MODEL,
null=False,
on_delete=models.PROTECT,
to_field="phone_number",
related_name="player_points"
)
points_ts = models.DateTimeField(auto_now_add=True, null=False)
У меня также есть обработчик сигнала перед сохранением.
@receiver(signals.pre_save, sender=PlayerPoint)
if sender is PlayerPoint:
try:
current_point = PlayerPoint.objects.filter(player=instance.player).latest()
except PlayerPoint.DoesNotExist as pdne:
if "new player" in instance.operation_reason.lower():
print(f"{pdne} {instance.player} must be a new")
instance.operation_amount = 100
instance.points = int(instance.points) + int(instance.operation_amount)
else:
raise pdne
except Exception as e:
print(f"{e} while trying to get current_point of the player, stopping execution")
raise e
else:
if instance.operation == PlayerPoint.OPERATION_CHOICES[0][0]:
instance.points = int(current_point.points) + int(instance.operation_amount)
elif instance.operation == PlayerPoint.OPERATION_CHOICES[1][0]:
if int(current_point.points) < int(instance.operation_amount):
raise ValidationError(
message="not enough points",
params={"points": current_point.points},
code="invalid"
)
instance.points = int(current_point.points) - int(instance.operation_amount)
Как вы можете видеть, существует отношение внешнего ключа.
Перед запуском тестов, в setUp()
я создаю очки для всех игроков следующим образом:
class Top5PlayersViewTestCase(TestCase):
def setUp(self) -> None:
self.player_model = get_user_model()
self.test_client = Client(raise_request_exception=True)
self.player_list = list()
for i in range(0, 10):
x = self.player_model.objects.create_user(
phone_number=f"+2517{i}{i}{i}{i}{i}{i}{i}{i}",
first_name="test",
father_name="user",
grandfather_name="tokko",
email=f"test_user@tokko7{i}.com",
age="29",
password="password"
)
PlayerPoint.objects.create(
operation="ADD",
operation_reason="new player",
player=x
)
self.player_list.append(x)
counter = 500
for player in self.player_list:
counter += int(player.phone_number[-1:])
PlayerPoint.objects.create(
operation="ADD",
operation_amount=counter,
operation_reason="add for testing",
player=player
)
PlayerPoint.objects.create(
operation="ADD",
operation_amount=counter,
operation_reason="add for testing",
player=player
)
return super().setUp()
При выполнении кода на localhost я могу вызвать latest()
для получения последних очков каждого игрока и поместить их в список.
all_players = get_user_model().objects.filter(is_staff=False).prefetch_related('player_points')
top_5 = list()
for player in all_players:
try:
latest_points = player.player_points.latest()
except Exception as e:
print(f"{player} -- {e}")
messages.error(request, f"{player} {e}")
но когда я делаю это из тестового кода django, он выдает ошибку self.model.DoesNotExist( commons.models.PlayerPoint.DoesNotExist: PlayerPoint matching query does not exist.
Что я делаю не так?
Моя благодарность заранее.
Я думаю, что ваша проблема заключается в этой строке:
latest_points = player.player_points.latest()
В частности, latest()
. Как и get(), earliest() и latest() вызывают ошибку DoesNotExist, если не существует объекта с заданными параметрами.
Возможно, вам нужно добавить get_latest_by
в класс Meta
вашей модели. Возможно, попробуйте следующее:
class Player(AbstractUser):
...
class Meta:
get_latest_by = ['-joined_ts']
Если вы не хотите добавлять это в свою модель, вы можете сделать это напрямую:
latest_points = player.player_points.latest('-joined_ts')
если проблема в этом.
Изменить это:
counter += int(player.phone_number[-1:])
to
counter += int(player.phone_number[1:])
Полагаю, вы просто хотите взять часть номера телефона, а не '+' char.
Также, либо это:
top_5 = PlayerPoint.objects.order_by('-points')[:5]
должно быть:
top_5 = PlayerPoint.objects.order_by('-pk, -points_ts')[:5]
или вам нужно изменить get_latest_by
в вашем классе PlayerPoint
, Meta
на:
class Meta:
get_latest_by = ['pk', 'points']
Есть две проблемы.
- В вашем представлении
top_5
- это несортированный списокPlayer
объектов.
top_5 = sorted(top_5, key=lambda player: player.player_points.latest().points, reverse=True)[:5] # Add this
return render(request, "points_app/monthlytop5players.html", {"results": top_5[:5]})
- В вашем тесте
top_5
- это список (на самом делеQuerySet
) объектовPlayerPoint
.
results_pts = [player.player_points.latest() for player in test_results.context['results']] # Add this
for pt in top_5:
# self.assertIn(pt, test_results.context.get('results')) # Change this
self.assertIn(pt, results_pts) # to this