PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Bagaimana cara merekam peringkat harian untuk model di Django?

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:

  1. 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 mendapatkan Student contoh dari database. Mengapa memiliki Student contoh di tempat pertama dan tidak hanya mengakses database mentah?

  2. 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.

  3. 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.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bagaimana PostgreSQL menerapkan batasan UNIK / jenis indeks apa yang digunakannya?

  2. Peningkatan otomatis di liquibase

  3. tambahkan indeks pada bidang jsonb

  4. Cache rencana eksekusi untuk fungsi PL/pgSQL di PostgreSQL

  5. PostgreSQL:mengekspor data yang dihasilkan dari kueri SQL ke Excel/CSV