sql >> Database >  >> RDS >> PostgreSQL

Hoe noteer ik dagelijkse rankings voor een model in Django?

Ik zou iets voorstellen dat lijkt op wat e4c5 suggereerde , maar ik zou ook:

  • Genereer een index op de datum van de rangen zodat het verkrijgen van alle rangen op een enkele dag kan worden geoptimaliseerd.

  • Markeer de datum en leerling als unique_together . Dit voorkomt de mogelijkheid om twee rangen voor dezelfde student op dezelfde datum vast te leggen.

De modellen zouden er als volgt uitzien:

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 een volwaardige applicatie zou ik ook enige uniciteitsbeperkingen verwachten op Grade en Student maar het probleem in de vraag geeft niet genoeg details over deze modellen.

Je zou dan elke dag een taak kunnen uitvoeren met cron of welke taakbeheerder je ook wilt gebruiken (Celery is ook een optie), om een ​​commando als het volgende uit te voeren dat de rangen zou bijwerken volgens een bepaalde berekening en de oude records zou opschonen. De volgende code is een illustratie van hoe het kan. De echte code moet zo worden ontworpen dat deze over het algemeen idempotent is (de volgende code is niet omdat de rangberekening willekeurig is), zodat als de server opnieuw wordt opgestart tijdens een update, de opdracht gewoon opnieuw kan worden uitgevoerd. Hier is de code:

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

Waarom geen augurken?

Meerdere redenen:

  1. Het is een voortijdige optimalisatie en over het algemeen waarschijnlijk helemaal geen optimalisatie. Sommige bewerkingen kunnen sneller zijn, maar andere bewerkingen langzamer zal zijn. Als de rangen worden gepickt in een veld op Student dan betekent het laden van een specifieke student in het geheugen dat alle ranginformatie samen met die student in het geheugen wordt geladen. Dit kan worden verholpen door .values() . te gebruiken of .values_list() maar dan krijg je geen Student meer exemplaren uit de database. Waarom hebben Student instanties in de eerste plaats en niet alleen toegang tot de onbewerkte database?

  2. Als ik de velden verander in Rank , maken Django's migratiefaciliteiten het gemakkelijk om de benodigde wijzigingen door te voeren wanneer ik de nieuwe versie van mijn applicatie implementeer. Als de ranginformatie in een veld wordt gepickt, moet ik elke structuurwijziging beheren door aangepaste code te schrijven.

  3. De databasesoftware heeft geen toegang tot waarden in een augurk en daarom moet u aangepaste code schrijven om toegang te krijgen. Als u met het bovenstaande model studenten per rang wilt weergeven (en de rangorde voor vandaag is al berekend), dan kunt u het volgende doen:

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

    Als je augurken gebruikt, moet je alle Students scan scannen en maak de rangen los om degene voor de gewenste dag te extraheren, en gebruik vervolgens een Python-gegevensstructuur om de studenten op rang te ordenen. Zodra dit is gebeurd, moet u deze structuur herhalen om de namen op volgorde te krijgen.



  1. Login-authenticatie naar een mysql-database op afstand

  2. RDSdataService execute_statement retourneert (BadRequestException)

  3. MySQL - index met meerdere kolommen

  4. Datum in .csv-bestand converteren naar SQL-indeling voordat massaal wordt ingevoegd