added on_delete in models an migrations for django 2.0
compatibility.
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('events', '0005_auto_20150907_2021'),
|
||||
@@ -17,15 +16,19 @@ class Migration(migrations.Migration):
|
||||
name='EventRanking',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID',
|
||||
serialize=False, auto_created=True, primary_key=True)),
|
||||
('placement', models.PositiveIntegerField(null=True, blank=True)),
|
||||
serialize=False, auto_created=True,
|
||||
primary_key=True)),
|
||||
('placement',
|
||||
models.PositiveIntegerField(null=True, blank=True)),
|
||||
('avg_placement', models.FloatField(default=4)),
|
||||
('avg_score', models.FloatField(default=0)),
|
||||
('hanchan_count', models.PositiveIntegerField(default=0)),
|
||||
('good_hanchans', models.PositiveIntegerField(default=0)),
|
||||
('won_hanchans', models.PositiveIntegerField(default=0)),
|
||||
('event', models.ForeignKey(to='events.Event')),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
||||
('event', models.ForeignKey(to='events.Event',
|
||||
on_delete=models.CASCADE)),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE)),
|
||||
],
|
||||
options={
|
||||
'ordering': ('placement', 'avg_placement', '-avg_score'),
|
||||
@@ -35,10 +38,13 @@ class Migration(migrations.Migration):
|
||||
name='Hanchan',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID',
|
||||
serialize=False, auto_created=True, primary_key=True)),
|
||||
serialize=False, auto_created=True,
|
||||
primary_key=True)),
|
||||
('start', models.DateTimeField(
|
||||
help_text='Wichtig damit die richtigen Hanchans in die Wertung kommen.', verbose_name='Beginn')),
|
||||
('player1_input_score', models.IntegerField(verbose_name='Punkte')),
|
||||
help_text='Wichtig damit die richtigen Hanchans in die Wertung kommen.',
|
||||
verbose_name='Beginn')),
|
||||
('player1_input_score',
|
||||
models.IntegerField(verbose_name='Punkte')),
|
||||
('player1_game_score', models.PositiveIntegerField(
|
||||
default=0, verbose_name='Punkte', editable=False)),
|
||||
('player1_placement', models.PositiveSmallIntegerField(
|
||||
@@ -50,8 +56,11 @@ class Migration(migrations.Migration):
|
||||
('player1_bonus_points', models.SmallIntegerField(
|
||||
null=True, editable=False, blank=True)),
|
||||
('player1_comment', models.CharField(verbose_name='Anmerkung',
|
||||
max_length=255, editable=False, blank=True)),
|
||||
('player2_input_score', models.IntegerField(verbose_name='Punkte')),
|
||||
max_length=255,
|
||||
editable=False,
|
||||
blank=True)),
|
||||
('player2_input_score',
|
||||
models.IntegerField(verbose_name='Punkte')),
|
||||
('player2_game_score', models.PositiveIntegerField(
|
||||
default=0, verbose_name='Punkte', editable=False)),
|
||||
('player2_placement', models.PositiveSmallIntegerField(
|
||||
@@ -63,8 +72,11 @@ class Migration(migrations.Migration):
|
||||
('player2_bonus_points', models.SmallIntegerField(
|
||||
null=True, editable=False, blank=True)),
|
||||
('player2_comment', models.CharField(verbose_name='Anmerkung',
|
||||
max_length=255, editable=False, blank=True)),
|
||||
('player3_input_score', models.IntegerField(verbose_name='Punkte')),
|
||||
max_length=255,
|
||||
editable=False,
|
||||
blank=True)),
|
||||
('player3_input_score',
|
||||
models.IntegerField(verbose_name='Punkte')),
|
||||
('player3_game_score', models.PositiveIntegerField(
|
||||
default=0, verbose_name='Punkte', editable=False)),
|
||||
('player3_placement', models.PositiveSmallIntegerField(
|
||||
@@ -76,8 +88,11 @@ class Migration(migrations.Migration):
|
||||
('player3_bonus_points', models.SmallIntegerField(
|
||||
null=True, editable=False, blank=True)),
|
||||
('player3_comment', models.CharField(verbose_name='Anmerkung',
|
||||
max_length=255, editable=False, blank=True)),
|
||||
('player4_input_score', models.IntegerField(verbose_name='Punkte')),
|
||||
max_length=255,
|
||||
editable=False,
|
||||
blank=True)),
|
||||
('player4_input_score',
|
||||
models.IntegerField(verbose_name='Punkte')),
|
||||
('player4_game_score', models.PositiveIntegerField(
|
||||
default=0, verbose_name='Punkte', editable=False)),
|
||||
('player4_placement', models.PositiveSmallIntegerField(
|
||||
@@ -89,22 +104,37 @@ class Migration(migrations.Migration):
|
||||
('player4_bonus_points', models.SmallIntegerField(
|
||||
null=True, editable=False, blank=True)),
|
||||
('player4_comment', models.CharField(verbose_name='Anmerkung',
|
||||
max_length=255, editable=False, blank=True)),
|
||||
('comment', models.TextField(verbose_name='Anmerkung', blank=True)),
|
||||
max_length=255,
|
||||
editable=False,
|
||||
blank=True)),
|
||||
('comment',
|
||||
models.TextField(verbose_name='Anmerkung', blank=True)),
|
||||
('confirmed', models.BooleanField(
|
||||
default=True, help_text='Nur g\xfcltige und best\xe4tigte Hanchans kommen in die Wertung.', verbose_name='Wurde best\xe4tigt')),
|
||||
('player_names', models.CharField(max_length=255, editable=False)),
|
||||
default=True,
|
||||
help_text='Nur g\xfcltige und best\xe4tigte Hanchans kommen in die Wertung.',
|
||||
verbose_name='Wurde best\xe4tigt')),
|
||||
('player_names',
|
||||
models.CharField(max_length=255, editable=False)),
|
||||
('season', models.PositiveSmallIntegerField(
|
||||
verbose_name='Saison', editable=False, db_index=True)),
|
||||
('event', models.ForeignKey(to='events.Event')),
|
||||
('event', models.ForeignKey(to='events.Event',
|
||||
on_delete=models.CASCADE)),
|
||||
('player1', models.ForeignKey(related_name='user_hanchan+',
|
||||
verbose_name='Spieler 1', to=settings.AUTH_USER_MODEL)),
|
||||
verbose_name='Spieler 1',
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE)),
|
||||
('player2', models.ForeignKey(related_name='user_hanchan+',
|
||||
verbose_name='Spieler 2', to=settings.AUTH_USER_MODEL)),
|
||||
verbose_name='Spieler 2',
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE)),
|
||||
('player3', models.ForeignKey(related_name='user_hanchan+',
|
||||
verbose_name='Spieler 3', to=settings.AUTH_USER_MODEL)),
|
||||
verbose_name='Spieler 3',
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE)),
|
||||
('player4', models.ForeignKey(related_name='user_hanchan+',
|
||||
verbose_name='Spieler 4', to=settings.AUTH_USER_MODEL)),
|
||||
verbose_name='Spieler 4',
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE)),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-start',),
|
||||
@@ -116,8 +146,11 @@ class Migration(migrations.Migration):
|
||||
name='KyuDanRanking',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID',
|
||||
serialize=False, auto_created=True, primary_key=True)),
|
||||
('dan', models.PositiveSmallIntegerField(null=True, blank=True)),
|
||||
serialize=False, auto_created=True,
|
||||
primary_key=True)),
|
||||
(
|
||||
'dan',
|
||||
models.PositiveSmallIntegerField(null=True, blank=True)),
|
||||
('dan_points', models.PositiveIntegerField(default=0)),
|
||||
('kyu', models.PositiveSmallIntegerField(
|
||||
default=10, null=True, blank=True)),
|
||||
@@ -128,7 +161,8 @@ class Migration(migrations.Migration):
|
||||
('legacy_date', models.DateField(null=True, blank=True)),
|
||||
('legacy_dan_points', models.PositiveIntegerField(default=0)),
|
||||
('legacy_kyu_points', models.PositiveIntegerField(default=0)),
|
||||
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)),
|
||||
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE)),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-dan', '-dan_points', '-kyu_points'),
|
||||
@@ -140,15 +174,19 @@ class Migration(migrations.Migration):
|
||||
name='SeasonRanking',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID',
|
||||
serialize=False, auto_created=True, primary_key=True)),
|
||||
('season', models.PositiveSmallIntegerField(verbose_name='Saison')),
|
||||
('placement', models.PositiveIntegerField(null=True, blank=True)),
|
||||
serialize=False, auto_created=True,
|
||||
primary_key=True)),
|
||||
('season',
|
||||
models.PositiveSmallIntegerField(verbose_name='Saison')),
|
||||
('placement',
|
||||
models.PositiveIntegerField(null=True, blank=True)),
|
||||
('avg_placement', models.FloatField(null=True, blank=True)),
|
||||
('avg_score', models.FloatField(null=True, blank=True)),
|
||||
('hanchan_count', models.PositiveIntegerField(default=0)),
|
||||
('good_hanchans', models.PositiveIntegerField(default=0)),
|
||||
('won_hanchans', models.PositiveIntegerField(default=0)),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE)),
|
||||
],
|
||||
options={
|
||||
'ordering': ('placement', 'avg_placement', '-avg_score'),
|
||||
|
||||
@@ -9,8 +9,8 @@ from datetime import datetime, time
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
@@ -29,8 +29,8 @@ class EventRanking(models.Model):
|
||||
Sie beschränken sich aber auf einen Event und werden nur dann angestossen,
|
||||
wenn der Event als Turnier markiert wurde.
|
||||
"""
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL)
|
||||
event = models.ForeignKey(Event)
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
|
||||
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
||||
placement = models.PositiveIntegerField(blank=True, null=True)
|
||||
avg_placement = models.FloatField(default=4)
|
||||
avg_score = models.FloatField(default=0)
|
||||
@@ -86,7 +86,7 @@ class Hanchan(models.Model):
|
||||
Es werden aber noch andere Tests durchgeführt, ob sie gültig ist.
|
||||
Außerdem gehört jede Hanchan zu einer Veranstaltung.
|
||||
"""
|
||||
event = models.ForeignKey(Event)
|
||||
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
||||
start = models.DateTimeField(
|
||||
_('Start'),
|
||||
help_text=_('This is crucial to get the right Hanchans that scores')
|
||||
@@ -94,7 +94,7 @@ class Hanchan(models.Model):
|
||||
|
||||
player1 = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
on_delete=models.PROTECT,
|
||||
related_name='user_hanchan+',
|
||||
verbose_name=_('Player 1'))
|
||||
player1_input_score = models.IntegerField(_('Score'))
|
||||
@@ -113,7 +113,7 @@ class Hanchan(models.Model):
|
||||
|
||||
player2 = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
on_delete=models.PROTECT,
|
||||
related_name='user_hanchan+',
|
||||
verbose_name=_('Player 2'))
|
||||
player2_input_score = models.IntegerField(_('Score'))
|
||||
@@ -132,7 +132,7 @@ class Hanchan(models.Model):
|
||||
|
||||
player3 = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
on_delete=models.PROTECT,
|
||||
related_name='user_hanchan+',
|
||||
verbose_name=_('Player 3'))
|
||||
player3_input_score = models.IntegerField(_('Score'))
|
||||
@@ -151,7 +151,7 @@ class Hanchan(models.Model):
|
||||
|
||||
player4 = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
on_delete=models.PROTECT,
|
||||
related_name='user_hanchan+',
|
||||
verbose_name=_('Player 4'))
|
||||
player4_input_score = models.IntegerField(_('Score'))
|
||||
@@ -335,18 +335,21 @@ class KyuDanRanking(models.Model):
|
||||
Im Gegensatz zum Ladder Ranking ist das nicht Saison gebunden.
|
||||
Deswegen läuft es getrennt.
|
||||
"""
|
||||
user = models.OneToOneField(settings.AUTH_USER_MODEL)
|
||||
user = models.OneToOneField(settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE)
|
||||
dan = models.PositiveSmallIntegerField(blank=True, null=True)
|
||||
dan_points = models.PositiveIntegerField(default=0)
|
||||
max_dan_points = models.PositiveIntegerField(default=0)
|
||||
kyu = models.PositiveSmallIntegerField(default=10, blank=True, null=True)
|
||||
kyu_points = models.PositiveIntegerField(default=0)
|
||||
won_hanchans = models.PositiveIntegerField(default=0)
|
||||
good_hanchans = models.PositiveIntegerField(default=0)
|
||||
hanchan_count = models.PositiveIntegerField(default=0)
|
||||
legacy_date = models.DateField(blank=True, null=True)
|
||||
legacy_hanchan_count = models.PositiveIntegerField(default=0)
|
||||
legacy_dan = models.PositiveSmallIntegerField(blank=True, null=True)
|
||||
legacy_dan_points = models.PositiveIntegerField(default=0)
|
||||
legacy_kyu_points = models.PositiveIntegerField(default=0)
|
||||
legacy_hanchan_count = models.PositiveIntegerField(default=0)
|
||||
wins_in_a_row = models.PositiveIntegerField(default=0)
|
||||
last_hanchan_date = models.DateTimeField(blank=True, null=True)
|
||||
objects = managers.KyuDanRankingManager()
|
||||
@@ -356,8 +359,8 @@ class KyuDanRanking(models.Model):
|
||||
verbose_name = _(u'Kyū/Dan Ranking')
|
||||
verbose_name_plural = _(u'Kyū/Dan Rankings')
|
||||
|
||||
def __unicode__(self):
|
||||
if self.dan_points is not None:
|
||||
def __str__(self):
|
||||
if self.dan is not None:
|
||||
return u"%s - %d. Dan" % (self.user.username, self.dan or 1)
|
||||
else:
|
||||
return u"%s - %d. Kyu" % (self.user.username, self.kyu or 10)
|
||||
@@ -395,7 +398,6 @@ class KyuDanRanking(models.Model):
|
||||
self.dan_points += bonus_points
|
||||
self.wins_in_a_row = 0
|
||||
|
||||
# TODO: Komplett Überabreiten!
|
||||
def append_tournament_bonuspoints(self, hanchan):
|
||||
"""
|
||||
Prüft ob es die letzte Hanchan in einem Turnier war. Wenn ja werden
|
||||
@@ -408,29 +410,29 @@ class KyuDanRanking(models.Model):
|
||||
user=self.user, event=hanchan.event
|
||||
).order_by('-start')
|
||||
last_hanchan_this_event = hanchans_this_event[0]
|
||||
if hanchan != last_hanchan_this_event:
|
||||
# Das braucht nur am Ende eines Turnieres gemacht werden.
|
||||
return False
|
||||
else:
|
||||
event_ranking = EventRanking.objects.get(
|
||||
user=self.user,
|
||||
event=hanchan.event
|
||||
)
|
||||
if event_ranking.placement == 1:
|
||||
bonus_points += 4
|
||||
hanchan.player_comment += u'+4 Punkte Turnier gewonnen. '
|
||||
if event_ranking.avg_placement == 1:
|
||||
bonus_points += 8
|
||||
hanchan.player_comment += u'+8 Pkt: alle Spiele des Turnieres gewonnen. '
|
||||
# Das braucht nur am Ende eines Turnieres gemacht werden.
|
||||
if hanchan != last_hanchan_this_event: return False
|
||||
event_ranking = EventRanking.objects.get(
|
||||
user=self.user,
|
||||
event=hanchan.event
|
||||
)
|
||||
if event_ranking.placement == 1:
|
||||
bonus_points += settings.TOURNAMENT_WIN_BONUSPOINTS
|
||||
hanchan.player_comment += u'+{0:d} Punkte Turnier gewonnen. '.format(
|
||||
settings.TOURNAMENT_WIN_BONUSPOINTS)
|
||||
if event_ranking.avg_placement == 1:
|
||||
bonus_points += settings.TOURNAMENT_FLAWLESS_VICTORY_BONUSPOINTS
|
||||
hanchan.player_comment += u'+{0:d} Pkt: alle Spiele des Turnieres gewonnen. '.format(
|
||||
settings.TOURNAMENT_FLAWLESS_VICTORY_BONUSPOINTS)
|
||||
|
||||
if bonus_points and self.dan:
|
||||
hanchan.dan_points += bonus_points
|
||||
self.dan_points += bonus_points
|
||||
elif bonus_points:
|
||||
hanchan.kyu_points += bonus_points
|
||||
self.kyu_points += bonus_points
|
||||
hanchan.bonus_points += bonus_points
|
||||
return True
|
||||
if bonus_points and self.dan:
|
||||
hanchan.dan_points += bonus_points
|
||||
self.dan_points += bonus_points
|
||||
elif bonus_points:
|
||||
hanchan.kyu_points += bonus_points
|
||||
self.kyu_points += bonus_points
|
||||
hanchan.bonus_points += bonus_points
|
||||
return True
|
||||
|
||||
def get_absolute_url(self):
|
||||
if self.dan or self.dan_points > 0:
|
||||
@@ -451,7 +453,7 @@ class KyuDanRanking(models.Model):
|
||||
force_recalc = True
|
||||
if force_recalc:
|
||||
# Setze alles auf die legacy Werte und berechne alles von neuem.
|
||||
self.dan = None
|
||||
self.dan = self.legacy_dan
|
||||
self.dan_points = self.legacy_dan_points or 0
|
||||
self.kyu = None
|
||||
self.kyu_points = self.legacy_kyu_points or 0
|
||||
@@ -507,14 +509,14 @@ class KyuDanRanking(models.Model):
|
||||
'score: %(score)d, kyu points: %(kyu_points)d, dan points: '
|
||||
'%(dan_points)d, bonus points: %(bonus_points)d',
|
||||
{'id': hanchan.pk, 'start': hanchan.start,
|
||||
'placement': hanchan.placement, 'score': hanchan.game_score,
|
||||
'placement': hanchan.placement,
|
||||
'score': hanchan.game_score,
|
||||
'kyu_points': hanchan.kyu_points or 0,
|
||||
'dan_points': hanchan.dan_points or 0,
|
||||
'bonus_points': hanchan.bonus_points or 0}
|
||||
)
|
||||
self.save(force_update=True)
|
||||
|
||||
|
||||
def update_hanchan_points(self, hanchan):
|
||||
"""
|
||||
Berechne die Kyu bzw. Dan Punkte für eine Hanchan neu.
|
||||
@@ -523,7 +525,7 @@ class KyuDanRanking(models.Model):
|
||||
"""
|
||||
hanchan.kyu_points = None
|
||||
hanchan.dan_points = None
|
||||
if hanchan.event.mahjong_tournament:
|
||||
if hanchan.event.mahjong_tournament and settings.TOURNAMENT_POINT_SYSTEM:
|
||||
"""Für Turniere gelten andere Regeln zur Punktevergabe:
|
||||
1. Platz 4 Punkte
|
||||
2. Platz 3 Punkte
|
||||
@@ -547,6 +549,7 @@ class KyuDanRanking(models.Model):
|
||||
hanchan.dan_points = -1
|
||||
elif hanchan.placement == 4:
|
||||
hanchan.dan_points = -2
|
||||
# otherwise player must be in the kyu ranking
|
||||
elif hanchan.game_score >= 60000:
|
||||
hanchan.kyu_points = 3
|
||||
elif hanchan.game_score >= 30000:
|
||||
@@ -568,38 +571,30 @@ class KyuDanRanking(models.Model):
|
||||
hanchan.kyu_points -= (self.kyu_points + hanchan.kyu_points)
|
||||
self.kyu_points += hanchan.kyu_points
|
||||
|
||||
|
||||
# TODO: Merkwürdige Methode die zwar funktioniert aber nicht sehr
|
||||
# aussagekräfig ist. Überarbeiten?
|
||||
def update_rank(self):
|
||||
if self.dan and self.dan_points < 0:
|
||||
self.dan_points = 0
|
||||
self.dan = 1
|
||||
print(self.user, self.dan, self.kyu)
|
||||
old_dan = self.dan
|
||||
if settings.DAN_ALLOW_DROP_DOWN and (self.dan or self.dan_points > 0):
|
||||
self.dan = max((dan for min_points, dan in settings.DAN_RANKS if
|
||||
self.dan_points > min_points))
|
||||
elif self.dan or self.dan_points > 0:
|
||||
old_dan = self.dan
|
||||
for min_points, dan_rank in settings.DAN_RANKS:
|
||||
if self.dan_points > min_points:
|
||||
self.dan = dan_rank
|
||||
break
|
||||
if old_dan is None or self.dan > old_dan:
|
||||
self.wins_in_a_row = 0
|
||||
elif self.kyu_points < 1:
|
||||
self.kyu_points = 0
|
||||
self.kyu = 10
|
||||
self.dan = max((dan for min_points, dan in settings.DAN_RANKS if
|
||||
self.max_dan_points > min_points))
|
||||
elif self.kyu_points > 50:
|
||||
self.dan = 1
|
||||
self.kyu = None
|
||||
self.dan_points = 0
|
||||
self.kyu = None
|
||||
self.kyu_points = 0
|
||||
self.wins_in_a_row = 0
|
||||
else:
|
||||
for min_points, kyu_rank in settings.KYU_RANKS:
|
||||
if self.kyu_points > min_points:
|
||||
self.kyu = kyu_rank
|
||||
break
|
||||
print(self, self.kyu_points)
|
||||
self.kyu = max((kyu for min_points, kyu in settings.KYU_RANKS if
|
||||
self.kyu_points > min_points))
|
||||
self.wins_in_a_row = 0 if self.dan > old_dan else self.wins_in_a_row
|
||||
|
||||
|
||||
class SeasonRanking(models.Model):
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL)
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
|
||||
season = models.PositiveSmallIntegerField(_('Season'))
|
||||
placement = models.PositiveIntegerField(blank=True, null=True)
|
||||
avg_placement = models.FloatField(blank=True, null=True)
|
||||
|
||||
Reference in New Issue
Block a user