6

我正在 Django 中开发日历应用程序。

相关模型结构如下:

class Lesson(models.Model):
  RECURRENCE_CHOICES = (
    (0, 'None'),
    (1, 'Daily'),
    (7, 'Weekly'),
    (14, 'Biweekly')
  )
  frequency = models.IntegerField(choices=RECURRENCE_CHOICES)
  lessonTime = models.TimeField('Lesson Time')
  startDate = models.DateField('Start Date')
  endDate = models.DateField('End Date')
  student = models.ForeignKey(Student)

class CancelledLesson(models.Model):
  lesson = models.ForeignKey(Lesson)
  student = models.ForeignKey(Student)
  cancelledLessonDate = models.DateField() # Actual date lesson has been cancelled, this is startDate + Frequency

class PaidLesson(models.Model):
  lesson = models.ForeignKey(Lesson)
  student = models.ForeignKey(Student)
  actualDate = models.DateField() # Actual date lesson took place
  paidAmt = models.DecimalField('Amount Paid', max_digits=5, decimal_places=2)
  paidDate = models.DateField('date paid')

class CompositeLesson(models.Model):
  # only used to aggregate lessons for individual lesson management
  lesson = models.ForeignKey(Lesson)
  student = models.ForeignKey(Student)
  actualDate = models.DateTimeField()
  isCancelled = models.BooleanField()
  canLesson = models.ForeignKey(CancelledLesson, blank=True, null=True)
  payLesson = models.ForeignKey(PaidLesson, blank=True, null=True)

显然,这都会导致显示属于特定学生的课程出现问题。我正在尝试做的是显示一个表格,其中显示学生姓名以及所有预定课程的实例。我正在动态计算重复频率以避免炸毁我的数据库。重复的例外情况(即课程取消)存储在它们自己的表中。生成重复时,将根据取消的课程表检查重复。

在此处查看我的代码以生成重复(以及由此引起的问题的小目录):Can't get key to display in Django template

我对 Python 相对缺乏经验,并且正在使用这个项目来了解很多概念,所以如果我遗漏了一些本质上是“Pythonic”的东西,我深表歉意。

4

2 回答 2

8

您的问题的关键部分是您使用少数模型来跟踪一个概念,因此您引入了很多重复和复杂性。每个附加模型都是 的“类型” Lesson,因此您应该在此处使用继承。此外,大多数附加模型只是跟踪 a 的特定特征,Lesson因此实际上不应该是模型本身。这就是我设置它的方式:

class Lesson(models.Model):
    RECURRENCE_CHOICES = (
        (0, 'None'),
        (1, 'Daily'),
        (7, 'Weekly'),
        (14, 'Biweekly')
    )

    student = models.ForeignKey(Student)
    frequency = models.IntegerField(choices=RECURRENCE_CHOICES)
    lessonTime = models.TimeField('Lesson Time')
    startDate = models.DateField('Start Date')
    endDate = models.DateField('End Date')
    cancelledDate = models.DateField('Cancelled Date', blank=True, null=True)
    paidAmt = models.DecimalField('Amount Paid', max_digits=5, decimal_places=2, blank=True, null=True)
    paidDate = models.DateField('Date Paid', blank=True, null=True)

class CancelledLessonManager(models.Manager):
    def get_query_set(self):
        return self.filter(cancelledDate__isnull=False)

class CancelledLesson(Lesson):
    class Meta:
        proxy = True

     objects = CancelledLessonManager()

class PaidLessonManager(models.Manager):
    def get_query_set(self):
        return self.filter(paidDate__isnull=False)

class PaidLesson(Lesson):
      class Meta:
          proxy = True

      objects = PaidLessonManager()

您会注意到我将所有属性都移到了Lesson. 这是应该的方式。例如,Lesson有一个cancelledDate字段。如果该字段为 NULL,则它不会被取消。如果是实际日期,则取消。不需要其他模型。

CancelledLesson但是,PaidLesson出于指导目的,我已经离开了。这些现在在 Django “代理模型”中被称为。他们没有自己的数据库表(所以没有讨厌的数据重复)。它们纯粹是为了方便。每个都有一个自定义管理器来返回适当的匹配Lessons,例如,您可以CancelledLesson.objects.all()只获取那些Lesson被取消的 s。您还可以使用代理模型在管理员中创建独特的视图。如果您只想为CancelledLessons 提供一个管理区域,则可以,而所有数据仍会进入一个表中Lesson

CompositeLesson已经过去了,很好的摆脱。这是试图将这三个其他模型组合成一个有凝聚力的东西的产物。这不再是必需的,因此您的查询将变得更加容易。

编辑

我没有提到您可以而且应该向Lesson模型添加实用方法。例如,虽然从数据库的角度来看,通过字段是否为 NULL 来跟踪取消/否是有意义的,但从编程的角度来看,它并不像它应该的那样直观。因此,您可能想要执行以下操作:

@property
def is_cancelled(self):
    return self.cancelledDate is not None

...

if lesson.is_cancelled:
   print 'This lesson is cancelled'

或者:

import datetime

...

def cancel(self, date=None, commit=True):
    self.cancelledDate = date or datetime.date.today()
    if commit:
        self.save()

然后,您可以通过调用取消课程lesson.cancel(),它会默认为今天取消。如果您想将来取消它,您可以传递一个日期:lesson.cancel(date=tommorrow)tomorrowa在哪里datetime)。如果您想在保存之前进行其他处理,请 pass commit=False,它实际上不会将对象保存到数据库中。然后,lesson.save()当你准备好时打电话。

于 2012-06-07T19:59:59.957 回答
1

这就是我想出的。我觉得 classes_in_range() 可能不像我能理解的那样“Pythonic”,但这正是我需要它做的。

class Lesson(models.Model):
RECURRENCE_CHOICES = (
    (0, 'None'),
    (1, 'Daily'),
    (7, 'Weekly'),
    (14, 'Biweekly')
)
relatedLesson = models.ForeignKey('self', null=True, blank=True)
student = models.ForeignKey(Student)
frequency = models.IntegerField(choices=RECURRENCE_CHOICES, null=True, blank=True)
lessonTime = models.TimeField('Lesson Time', null=True, blank=True)
startDate = models.DateField('Start Date')
endDate = models.DateField('End Date', null=True, blank=True)
isCancelled = models.BooleanField(default = False)
amtBilled = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
amtPaid = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)

def get_exceptions(self):
    return Lesson.objects.filter(relatedLesson = self.id)

def cancel(self, date=None):
    if date:
        x = Lesson()
        x = self
        x.pk = None
        x.relatedLesson = self.id
        x.isCancelled = True
        x.startDate = date
        x.endDate = date
        x.save()
    else:
        self.endDate = datetime.date.today()
        self.save()
    return

def pay_lesson(self, date, amount):
    x = Lesson()
    x = self
    x.pk = None
    x.relatedLesson = self.id
    x.amtPaid = amount
    x.startDate = date
    x.endDate = date
    x.save()
    return

def lessons_in_range(self, startDate, endDate):
    if (self.startDate > endDate) or (self.endDate < startDate):
        return None
    if self.endDate < endDate:
        endDate = self.endDate
    ex = self.get_exceptions()
    if self.frequency == 0:
        if ex:
            return ex
        else:
            return self
    sd = next_date(self.startDate, self.frequency, startDate)
    lessonList = []
    while (sd <= endDate):
        exf = ex.filter(startDate = sd)
        if exf:
            # lesson already exists in database, add it
            lessonList.append(exf)
        elif sd == self.startDate:
            # lesson is the original lesson, add that
            lessonList.append(self)
        else:
            # lesson does not exist, create it in the database then add it to the list
            x = Lesson()
            x.student = self.student
            x.frequency = 0
            x.lessonTime = self.lessonTime
            x.relatedLesson = self
            x.startDate = sd
            x.endDate = sd
            x.isCancelled = False
            x.amtBilled = self.amtBilled    
            x.amtPaid = None
            x.save()
            lessonList.append(x)
        sd += timedelta(self.frequency)
    return lessonList
于 2012-06-10T19:13:38.420 回答