Бих предложил нещо подобно на това, което e4c5 предложи , но също така бих:
-
Генерирайте индекс за датата на класирането, така че получаването на всички рангове за всеки един ден да може да бъде оптимизирано.
-
Маркирайте датата и ученика като
unique_together
. Това предотвратява възможността за записване на две оценки за един и същи ученик на една и съща дата.
Моделите ще изглеждат така:
from django.db import models
class Grade(models.Model):
pass # Whatever you need here...
class Student(models.Model):
name = models.CharField(max_length=20)
grade = models.ForeignKey(Grade)
class Rank(models.Model):
class Meta(object):
unique_together = (("date", "student"), )
date = models.DateField(db_index=True)
student = models.ForeignKey(Student)
value = models.IntegerField()
В пълноценно приложение бих очаквал да има някои ограничения за уникалност на Grade
и Student
но проблемът, представен във въпроса, не предоставя достатъчно подробности за тези модели.
След това можете да изпълнявате задача всеки ден с cron
или какъвто и да е мениджър на задачи, който искате да използвате (Celery също е опция), за да изпълните команда като следната, която ще актуализира ранговете според някои изчисления и ще изчисти старите записи. Следният код е илюстрация как може да се направи. Истинският код трябва да бъде проектиран така, че като цяло да бъде идемпотентен (следният код не е такъв, защото изчислението на ранга е произволно), така че ако сървърът се рестартира по време на актуализация, командата може просто да се изпълни отново. Ето кода:
import random
import datetime
from optparse import make_option
from django.utils.timezone import utc
from django.core.management.base import BaseCommand
from school.models import Rank, Student
def utcnow():
return datetime.datetime.utcnow().replace(tzinfo=utc)
class Command(BaseCommand):
help = "Compute ranks and cull the old ones"
option_list = BaseCommand.option_list + (
make_option('--fake-now',
default=None,
help='Fake the now value to X days ago.'),
)
def handle(self, *args, **options):
now = utcnow()
fake_now = options["fake_now"]
if fake_now is not None:
now -= datetime.timedelta(days=int(fake_now))
print "Setting now to: ", now
for student in Student.objects.all():
# This simulates a rank computation for the purpose of
# illustration.
rank_value = random.randint(1, 1000)
try:
rank = Rank.objects.get(student=student, date=now)
except Rank.DoesNotExist:
rank = Rank(
student=student, date=now)
rank.value = rank_value
rank.save()
# Delete all ranks older than 180 days.
Rank.objects.filter(
date__lt=now - datetime.timedelta(days=180)).delete()
Защо не кисели краставички?
Няколко причини:
-
Това е преждевременна оптимизация и като цяло вероятно изобщо не е оптимизация. Някои операциите може да са по-бързи, но други операции ще бъде по-бавно. Ако ранговете са избрани в поле на
Student
тогава зареждането на конкретен ученик в паметта означава зареждане на цялата информация за ранга в паметта заедно с този ученик. Това може да бъде смекчено чрез използване на.values()
или.values_list()
но тогава вече не получаватеStudent
екземпляри от базата данни. Защо да иматеStudent
инстанции на първо място, а не просто достъп до необработената база данни? -
Ако променя полетата в
Rank
, средствата за миграция на Django позволяват лесно извършване на необходимите промени, когато разположа новата версия на моето приложение. Ако информацията за ранга е избрана в поле, трябва да управлявам всяка промяна на структурата, като напиша персонализиран код. -
Софтуерът на базата данни не може да има достъп до стойности в туршия и затова трябва да напишете персонализиран код за достъп до тях. С модела по-горе, ако искате да изброите учениците по ранг днес (а ранговете за днес вече са изчислени), тогава можете да направите:
for r in Rank.objects.filter(date=utcnow()).order_by("value")\ .prefetch_related(): print r.student.name
Ако използвате кисели краставици, трябва да сканирате всички
Students
и махнете ранговете, за да извлечете този за деня, който искате, и след това използвайте структура от данни на Python, за да подредите учениците по ранг. След като това е направено, трябва да преминете през тази структура, за да подредите имената.