sql >> Databasteknik >  >> RDS >> PostgreSQL

Hur registrerar jag dagliga rankningar för en modell i Django?

Jag skulle föreslå något liknande det som e4c5 föreslog , men jag skulle också:

  • Generera ett index på datumet för rankningarna så att alla rankningar på en enskild dag kan optimeras.

  • Markera datumet och studenten som unique_together . Detta förhindrar möjligheten att spela in två rankningar för samma elev på samma datum.

Modellerna skulle se ut så här:

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

I en fullfjädrad applikation skulle jag också förvänta mig att ha några unika begränsningar på Grade och Student men problemet som presenteras i frågan ger inte tillräckligt med detaljer om dessa modeller.

Du kan sedan köra en uppgift varje dag med cron eller vilken aktivitetshanterare du än vill använda (Sellery är också ett alternativ), för att köra ett kommando som det följande som skulle uppdatera rangordningen enligt någon beräkning och rensa de gamla posterna. Följande kod är en illustration hur det kan göras. Den riktiga koden bör utformas för att vara allmänt idempotent (följande kod beror inte på att rangberäkningen är slumpmässig) så att om servern startas om mitt i en uppdatering kan kommandot bara köras om. Här är koden:

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

Varför inte pickles?

Flera skäl:

  1. Det är en för tidig optimering, och överlag förmodligen ingen optimering alls. Vissa operationer kan vara snabbare, men andra operationer kommer att gå långsammare. Om rangen är inlagda i ett fält på Student sedan, att ladda en specifik elev i minnet innebär att ladda all ranginformation i minnet tillsammans med den eleven. Detta kan mildras genom att använda .values() eller .values_list() men då får du inte längre Student instanser från databasen. Varför ha Student instanser i första hand och inte bara få tillgång till rådatabasen?

  2. Om jag ändrar fälten i Rank , Djangos migreringsfaciliteter gör det enkelt att utföra de nödvändiga ändringarna när jag distribuerar den nya versionen av min applikation. Om ranginformationen är inlagd i ett fält måste jag hantera alla strukturändringar genom att skriva anpassad kod.

  3. Databasprogramvaran kan inte komma åt värden i en pickle och så du måste skriva anpassad kod för att komma åt dem. Med modellen ovan, om du vill lista elever efter rang idag (och rangordningen för idag har redan beräknats), kan du göra:

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

    Om du använder pickles måste du skanna alla Students och ta bort leden för att extrahera den för dagen du vill ha, och använd sedan en Python-datastruktur för att sortera eleverna efter rang. När detta är gjort måste du iterera över denna struktur för att få ordning på namnen.



  1. C# MySql SKAPA ANVÄNDARE

  2. Omedelbar filinitiering:Inverkan under installationen

  3. UnknownHostException när man försöker ansluta till heroku-postgres från det lokala Springboot-projektet med Spring JPA

  4. massuppdatera mysql-tabellens decimalvärde och ändra kolumnfältstyp