我正在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”的东西,我道歉。
答案 0 :(得分:5)
问题的关键部分在于您使用少数模型来跟踪一个概念,因此您会引入大量重复和复杂性。每个附加模型都是Lesson
的“类型”,因此您应该在此使用继承。此外,大多数附加模型仅仅跟踪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
个,例如。您还可以使用代理模型在管理员中创建唯一视图。如果您希望仅为CancelledLesson
设置管理区域,则所有数据仍会进入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)
(其中tomorrow
是datetime
)。如果要在保存之前进行其他处理,请传递commit=False
,它实际上不会将对象保存到数据库中。然后,在准备就绪时致电lesson.save()
。
答案 1 :(得分:1)
这就是我想出的。我觉得lesson_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