PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Come registro le classifiche giornaliere per un modello in Django?

Suggerirei qualcosa di simile a ciò che e4c5 suggerito , ma vorrei anche:

  • Genera un indice sulla data delle graduatorie in modo da ottimizzare l'ottenimento di tutte le graduatorie in un singolo giorno.

  • Contrassegna la data e lo studente come unique_together . Ciò impedisce la possibilità di registrare due graduatorie per lo stesso studente nella stessa data.

I modelli sarebbero così:

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()

In un'applicazione a tutti gli effetti mi aspetterei anche di avere alcuni vincoli di unicità su Grade e Student ma il problema presentato nella domanda non fornisce dettagli sufficienti su questi modelli.

Puoi quindi eseguire un'attività ogni giorno con cron o qualsiasi task manager tu voglia usare (anche il sedano è un'opzione), per eseguire un comando come il seguente che aggiornerebbe i ranghi in base ad alcuni calcoli ed eliminerebbe i vecchi record. Il codice seguente è un'illustrazione di come si può fare. Il codice reale dovrebbe essere progettato per essere generalmente idempotente (il codice seguente non è perché il calcolo del rango è casuale) in modo che se il server viene riavviato nel mezzo di un aggiornamento, il comando può essere semplicemente eseguito nuovamente. Ecco il codice:

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()

Perché non sottaceti?

Molteplici motivi:

  1. È un'ottimizzazione prematura e, nel complesso, probabilmente non è affatto un'ottimizzazione. Alcuni le operazioni possono essere più veloci, ma altre operazioni sarà più lento. Se le graduatorie vengono raccolte in un campo su Student quindi, caricare in memoria uno specifico studente significa caricare in memoria tutte le informazioni sul grado insieme a quello studente. Questo può essere mitigato usando .values() o .values_list() ma poi non ricevi più Student istanze dal database. Perché avere Student istanze in primo luogo e non solo accedere al database grezzo?

  2. Se cambio i campi in Rank , le funzionalità di migrazione di Django consentono facilmente di eseguire le modifiche necessarie quando distribuisco la nuova versione della mia applicazione. Se le informazioni sulla classifica vengono raccolte in un campo, devo gestire qualsiasi modifica alla struttura scrivendo un codice personalizzato.

  3. Il software del database non può accedere ai valori in un pickle e quindi è necessario scrivere un codice personalizzato per accedervi. Con il modello sopra, se vuoi elencare gli studenti in base al grado di oggi (e i gradi per oggi sono già stati calcolati), puoi fare:

    for r in Rank.objects.filter(date=utcnow()).order_by("value")\
        .prefetch_related():
        print r.student.name
    

    Se usi i sottaceti, devi scansionare tutti gli Students e deseleziona i gradi per estrarre quello per il giorno desiderato, quindi usa una struttura dati Python per ordinare gli studenti in base al grado. Fatto ciò, devi scorrere questa struttura per ottenere i nomi in ordine.