sql >> Databáze >  >> RDS >> PostgreSQL

Jak zaznamenám denní hodnocení modelky v Django?

Navrhoval bych něco podobného tomu, co e4c5 navrhl , ale také bych:

  • Vygenerujte index k datu hodnocení, aby bylo možné optimalizovat získání všech hodnocení v kterýkoli den.

  • Označte datum a studenta jako unique_together . To zabraňuje možnosti zaznamenat dvě hodnosti pro stejného studenta ve stejné datum.

Modely by vypadaly takto:

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

V plnohodnotné aplikaci bych také očekával určitá omezení jedinečnosti na Grade a Student ale problém uvedený v otázce neposkytuje dostatek podrobností o těchto modelech.

Pomocí cron pak můžete každý den spouštět úlohu nebo jakéhokoli správce úloh, který chcete použít (Celery je také možnost), ke spuštění příkazu jako je následující, který by aktualizoval pořadí podle nějakého výpočtu a vyčistil staré záznamy. Následující kód je ilustrační jak se to dá udělat. Skutečný kód by měl být navržen tak, aby byl obecně idempotentní (následující kód není proto, že výpočet pořadí je náhodný), takže pokud je server restartován uprostřed aktualizace, lze příkaz pouze znovu spustit. Zde je kód:

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

Proč ne okurky?

Více důvodů:

  1. Je to předčasná optimalizace a celkově pravděpodobně vůbec žádná optimalizace. Některé operace mohou být rychlejší, ale jiné operace bude pomalejší. Pokud jsou hodnosti vybrány do pole na Student pak načtení konkrétního studenta do paměti znamená načtení všech informací o hodnosti do paměti společně s tímto studentem. To lze zmírnit pomocí .values() nebo .values_list() ale pak už nedostanete Student instance z databáze. Proč mít Student instance na prvním místě a ne pouze přístup k nezpracované databázi?

  2. Pokud změním pole v Rank , migrační prostředky Django snadno umožňují provádění potřebných změn při nasazení nové verze mé aplikace. Pokud jsou informace o hodnosti vybrány do pole, musím řídit jakoukoli změnu struktury napsáním vlastního kódu.

  3. Databázový software nemůže přistupovat k hodnotám v nálevu, takže pro přístup k nim musíte napsat vlastní kód. S výše uvedeným modelem, pokud chcete seznam studentů podle hodnosti dnes (a hodnosti pro dnešek již byly spočítány), můžete:

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

    Pokud používáte okurky, musíte naskenovat všechny Students a rozbalte hodnosti, abyste extrahovali hodnosti pro požadovaný den, a pak použijte datovou strukturu Pythonu k seřazení studentů podle hodnosti. Jakmile to uděláte, musíte tuto strukturu iterovat, abyste dostali názvy do pořádku.



  1. Maximální velikost tabulky pro databázi MySQL

  2. SQL jak seskupit podle fiskálního čtvrtletí a roku s datovým polem

  3. Názvy sloupců a datové typy pro materializované pohledy v PostgreSQL?

  4. Odečtěte měsíc od data v MariaDB