如何在Django中记录模型的每日排名?

Riv*_*diz 0 python django postgresql database-design django-models

我有一个模型,我必须记录一个PositiveSmallIntegerField对象,每天更新相关的分数.

class Student(models.Model):
    name = models.CharField(max_length=20)
    grade = models.ForeignKey(Grade)
    rank = ??
Run Code Online (Sandbox Code Playgroud)

具有此模型的对象数量永远不会超过100,并且分数/等级必须保留180天.该数据库是Postgresql 9.2.

每天从另一个应用程序的分数计算排名,我想存储在与学生模型相关的数据库中,我坚持模型设计,我不知道,应该为排名做些什么?Django中有重复的字段吗?

任何线索或经验将非常感激

谢谢.

更新:(添加示例)

数据库必须看起来像这样,

+---------+-------+----------+----------+----------+----------+----------+----------+
| Student | Grade | 08-01-15 | 08-02-15 | 08-03-15 | 08-04-15 | 08-05-15 | 08-06-15 |
+---------+-------+----------+----------+----------+----------+----------+----------+
| Alex    |     5 |        2 |        1 |        1 |        2 |        3 |        2 |
| John    |     5 |        3 |        2 |        3 |        4 |        2 |        4 |
| Susan   |     5 |        1 |        4 |        2 |        1 |        1 |        1 |
| Zara    |     5 |        4 |        3 |        4 |        3 |        4 |        3 |
+---------+-------+----------+----------+----------+----------+----------+----------+
Run Code Online (Sandbox Code Playgroud)

学生的等级必须存储在这里显示的日期,对于第1天,等级必须存储在一个列/任何类似的列中,并且连续180天的天数必须持续,每个等级的等级必须在连续几天内添加一天.

我没有坚持使用save方法,而是关于保存计算排名的字段.

Lou*_*uis 9

我会建议类似于e4c5 建议的内容,但我也会:

  • 生成排名日期的索引,以便可以优化获得任何一天的所有排名.

  • 将日期和学生标记为unique_together.这可以防止在同一天为同一个学生录制两个等级的可能性.

模型看起来像这样:

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()
Run Code Online (Sandbox Code Playgroud)

在一个成熟的应用程序中,我也希望有一些唯一性约束Grade,Student但问题中提出的问题并未提供有关这些模型的足够详细信息.

然后,您可以每天运行任务cron或使用任何您想要使用的任务管理器(Celery也是一个选项),运行如下命令,根据某些计算更新排名并清除旧记录.以下代码说明了如何完成它.实际代码应该设计为通常是幂等的(以下代码不是因为秩计算是随机的),因此如果服务器在更新过程中重新启动,则可以重新运行该命令.这是代码:

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()
Run Code Online (Sandbox Code Playgroud)

为什么不泡菜?

多种原因:

  1. 这是一个不成熟的优化,总体上可能根本不是优化.某些操作可能更快,但其他操作会更慢.如果当时将等级腌制到字段中,Student则将特定学生加载到存储器中意味着将所有等级信息与该学生一起加载到存储器中.这可以通过使用.values()或减少,.values_list()但您不再Student从数据库中获取实例.为什么首先有Student实例而不只是访问原始数据库?

  2. 如果我更改了字段Rank,Django的迁移工具可以轻松地在部署新版本的应用程序时执行所需的更改.如果将排名信息腌制到字段中,我必须通过编写自定义代码来管理任何结构更改.

  3. 数据库软件无法访问pickle中的值,因此您必须编写自定义代码才能访问它们.使用上面的模型,如果你想今天按排名列出学生(并且已经计算了今天的排名),那么你可以这样做:

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

    如果您使用泡菜,则必须扫描所有Students并取消排序以获取所需的日期,然后使用Python数据结构按排名对学生进行排序.完成此操作后,您必须迭代此结构以按顺序获取名称.