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)

どうやらこれはすべて、特定の学生に属するレッスンの表示に問題を引き起こしています。私がやろうとしているのは、生徒の名前とスケジュールされたレッスンのすべてのインスタンスを示すテーブルを表示することです。データベースが破壊されないように、繰り返しを動的に計算しています。繰り返しの例外(つまり、レッスンのキャンセル)は、独自のテーブルに保存されます。繰り返しは、繰り返しが生成されるときに、キャンセルされたレッスンテーブルに対してチェックされます。

繰り返しを生成するための私のコード(およびこれが引き起こしている問題の小さなカタログ)をここで参照してください:Djangoテンプレートに表示するキーを取得できません

私はPythonに比較的不慣れであり、このプロジェクトを多くの概念に頭を悩ませる方法として使用しているので、本質的に「Pythonic」である何かを見逃している場合は、お詫び申し上げます。

4

2 に答える 2

8

問題の重要な部分は、少数のモデルを使用して1つの概念のみを追跡しているため、多くの重複と複雑さを導入していることです。追加の各モデルはの「タイプ」で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の場合、キャンセルされません。実際の日付の場合はキャンセルされます。別のモデルは必要ありません。

しかし、私は両方CancelledLessonPaidLesson教育目的で去りました。これらは現在、Djangoの「プロキシモデル」と呼ばれています。彼らは独自のデータベーステーブルを取得しません(したがって、厄介なデータの重複はありません)。それらは純粋に便宜のためです。それぞれに適切な一致を返すカスタムマネージャーがあるため、たとえば、キャンセルされたものだけを取得Lessonsできます。プロキシモデルを使用して、管理者に一意のビューを作成することもできます。s専用の管理領域が必要な場合は、すべてのデータを1つのテーブルに入れておくことができます。CancelledLesson.objects.all()LessonCancelledLessonLesson

CompositeLessonなくなって、良い馬鹿げています。これは、これら3つの他のモデルを1つのまとまりのあるものに構成しようとした結果です。これはもはや必要ではなく、結果としてクエリが劇的に簡単になります。

編集

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()準備ができたら電話します。

于 2012-06-07T19:59:59.957 に答える
1

これが私が思いついたものです。lessons_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 に答える