PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Jak rejestrować dzienne rankingi modelu w Django?

Sugerowałbym coś podobnego do tego, co e4c5 sugerował , ale chciałbym też:

  • Wygeneruj indeks na dzień rang, aby zoptymalizować uzyskanie wszystkich rang każdego dnia.

  • Oznacz datę i ucznia jako unique_together . Zapobiega to możliwości zapisania dwóch rang dla tego samego ucznia w tym samym dniu.

Modele wyglądałyby tak:

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

W pełnoprawnej aplikacji spodziewałbym się również pewnych ograniczeń dotyczących unikalności Grade i Student ale problem przedstawiony w pytaniu nie dostarcza wystarczających szczegółów na temat tych modeli.

Możesz wtedy codziennie uruchamiać zadanie za pomocą cron lub dowolnego menedżera zadań, którego chcesz użyć (celery jest również opcją), aby uruchomić polecenie podobne do poniższego, które zaktualizuje rangi zgodnie z niektórymi obliczeniami i usunie stare rekordy. Poniższy kod jest ilustracją jak można to zrobić. Prawdziwy kod powinien być zaprojektowany tak, aby był ogólnie idempotentny (poniższy kod nie jest, ponieważ obliczanie rangi jest losowe), tak aby w przypadku ponownego uruchomienia serwera w środku aktualizacji polecenie można było po prostu ponownie uruchomić. Oto kod:

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

Dlaczego nie marynaty?

Wiele powodów:

  1. Jest to przedwczesna optymalizacja, która prawdopodobnie wcale nie jest optymalizacją. Niektóre operacje mogą być szybsze, ale inne operacje będzie wolniejszy. Jeśli rangi są zamarynowane w polu na Student następnie załadowanie konkretnego ucznia do pamięci oznacza załadowanie wszystkich informacji o randze do pamięci wraz z tym uczniem. Można to złagodzić za pomocą .values() lub .values_list() ale wtedy nie otrzymujesz już Student instancje z bazy danych. Dlaczego Student instancje w pierwszej kolejności, a nie tylko dostęp do surowej bazy danych?

  2. Jeśli zmienię pola w Rank , narzędzia migracji Django z łatwością pozwalają na wykonanie potrzebnych zmian podczas wdrażania nowej wersji mojej aplikacji. Jeśli informacje o randze są zamarynowane w polu, muszę zarządzać każdą zmianą struktury, pisząc niestandardowy kod.

  3. Oprogramowanie bazy danych nie może uzyskać dostępu do wartości w marynacie, więc musisz napisać niestandardowy kod, aby uzyskać do nich dostęp. W powyższym modelu, jeśli chcesz wyświetlić listę uczniów według rangi dzisiaj (a rangi na dziś zostały już obliczone), możesz:

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

    Jeśli używasz pikli, musisz zeskanować wszystkich Students i rozbierz rangi, aby wyodrębnić ten na dany dzień, a następnie użyj struktury danych Pythona, aby uporządkować uczniów według rang. Gdy to zrobisz, musisz przejść przez tę strukturę, aby uporządkować nazwy.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak sprawić, by polecenie kopiowania kontynuowało działanie z przesunięciem ku czerwieni nawet po przekroczeniu limitu czasu funkcji lambda, która ją zainicjowała?

  2. Jak uzyskać dostęp do procedury zwracającej setof refcursor z PostgreSQL w Javie?

  3. Jak odświeżyć wszystkie zmaterializowane widoki w Postgresql 9.3 na raz?

  4. Jak mogę utworzyć niestandardowy typ kolumny za pomocą Typesafe Slick w Scali?

  5. UPDATE za pomocą ORDER BY