Saya akan menyarankan sesuatu yang mirip dengan apa yang e4c5 disarankan , tapi saya juga akan:
-
Hasilkan indeks pada tanggal peringkat sehingga perolehan semua peringkat dalam satu hari dapat dioptimalkan.
-
Tandai tanggal dan siswa sebagai
unique_together
. Ini mencegah kemungkinan merekam dua peringkat untuk siswa yang sama pada tanggal yang sama.
Modelnya akan terlihat seperti ini:
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()
Dalam aplikasi lengkap, saya juga berharap memiliki beberapa batasan keunikan pada Grade
dan Student
tetapi masalah yang disajikan dalam pertanyaan tidak memberikan detail yang cukup tentang model ini.
Anda kemudian dapat menjalankan tugas setiap hari dengan cron
atau pengelola tugas apa pun yang ingin Anda gunakan (Seledri juga merupakan opsi), untuk menjalankan perintah seperti berikut ini yang akan memperbarui peringkat menurut beberapa perhitungan dan membersihkan catatan lama. Kode berikut adalah ilustrasi tentang bagaimana hal itu dapat dilakukan. Kode yang sebenarnya harus dirancang untuk secara umum idempoten (kode berikut bukan karena perhitungan peringkat acak) sehingga jika server di-boot ulang di tengah pembaruan, perintah dapat dijalankan kembali. Ini kodenya:
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()
Mengapa tidak acar?
Beberapa alasan:
-
Ini adalah optimasi prematur, dan secara keseluruhan mungkin bukan optimasi sama sekali. Beberapa operasi mungkin lebih cepat, tetapi operasi lain akan lebih lambat. Jika peringkat diasamkan ke dalam bidang di
Student
kemudian, memuat siswa tertentu ke dalam memori berarti memuat semua informasi peringkat ke dalam memori bersama dengan siswa itu. Ini dapat dikurangi dengan menggunakan.values()
atau.values_list()
tetapi kemudian Anda tidak lagi mendapatkanStudent
contoh dari database. Mengapa memilikiStudent
contoh di tempat pertama dan tidak hanya mengakses database mentah? -
Jika saya mengubah bidang di
Rank
, fasilitas migrasi Django dengan mudah memungkinkan melakukan perubahan yang diperlukan ketika saya menyebarkan versi baru dari aplikasi saya. Jika informasi peringkat dimasukkan ke dalam bidang, saya harus mengelola perubahan struktur apa pun dengan menulis kode khusus. -
Perangkat lunak basis data tidak dapat mengakses nilai dalam acar sehingga Anda harus menulis kode khusus untuk mengaksesnya. Dengan model di atas, jika Anda ingin membuat daftar siswa berdasarkan peringkat hari ini (dan peringkat untuk hari ini sudah dihitung), maka Anda dapat melakukan:
for r in Rank.objects.filter(date=utcnow()).order_by("value")\ .prefetch_related(): print r.student.name
Jika Anda menggunakan acar, Anda harus memindai semua
Student
dan hapus peringkat untuk mengekstrak satu untuk hari yang Anda inginkan, dan kemudian gunakan struktur data Python untuk mengurutkan siswa berdasarkan peringkat. Setelah ini selesai, Anda harus mengulangi struktur ini untuk mendapatkan nama secara berurutan.