2012-02-28 5 views
2

私は関連する3つのモデルを持っています。最初のものはDayOfWeekと呼ばれ、1日のラベルと番号です。それは次のようになります。Django Many To Manyプライマリキーに不満があります

class Event(AnnouncementBase, Location): 
    cost = CurrencyField(decimal_places=2, max_digits=10, blank=True, default=0.00) 
    start_date = models.DateField(default = datetime.now().date()) 
    start_time = models.TimeField(default = datetime.now().time()) 
    end_date = models.DateField(blank=True, default=None, null = True) 
    end_time = models.TimeField(blank=True, default=None, null = True) 

最後に、再発がある:

class DayOfWeek(models.Model): 
    day = models.IntegerField() 
    label = models.CharField(max_length='20') 

    def __str__(self): 
    return self.label 

このクラスは、治具に私がsyncdb.Nextたびに使用して移入され、私はイベントモデルを持っている、それはこのようになります。イベントがあり、定期的なイベントのスケジュールを設定するために使用されます。それは次のようになります。テストは、以下の例外を除いて、それが失敗した実行されるたびに

def test_weekly_mon_wed_fri_occurrence(self): 
    event = Event() 
    event.start_date = date(year=2012, month=1, day=2) 
    event.start_time = time(hour=13, minute=30) 
    event.save() 

    recurrence = Recurrence() 
    recurrence.repeats = EVENT_REPEAT_CHOICES[1][0] 
    recurrence.repeat_on = (EVENT_DAY_CHOICES[1][0], EVENT_DAY_CHOICES[3][0], EVENT_DAY_CHOICES[5][0]) 
    recurrence.repeat_ends = EVENT_REPEAT_END_CHOICES[0][0] 
    recurrence.event = event 

    nextEvent = recurrence.getNextEvent(event) 

    self.assertEquals(date(year=2012, month=1, day=4), nextEvent.start_date) 
    self.assertEquals(event.start_time, nextEvent.start_time) 

    nextNextEvent = recurrence.getNextEvent(nextEvent) 

    self.assertEquals(date(year=2012, month=1, day=6), nextNextEvent.start_date) 
    self.assertEquals(event.start_time, nextNextEvent.start_time) 

class Recurrence(models.Model): 

    event = models.ForeignKey(Event, related_name='event') 
    repeats = models.CharField(max_length = 50, choices = EVENT_REPEAT_CHOICES) 
    repeat_every = models.IntegerField(default = 1) 

    repeat_on = models.ManyToManyField(DayOfWeek, blank=True, null=True) 

    repeat_by = models.CharField(max_length = 50, choices = EVENT_REPEAT_BY_CHOICES, blank=True) 
    repeat_by_day_of_month = models.IntegerField(default = 0, blank=True) 

    repeat_ends = models.CharField(max_length = 50, choices = EVENT_REPEAT_END_CHOICES) 
    end_occurrences = models.IntegerField(default = 0, blank=True) 
    repeat_end_date = models.DateField(blank=True, default=None, null = True) 

    past_event_count = models.IntegerField(default=0, blank=True) 
    scheduled_events = models.ManyToManyField(Event, blank=True, default=None, related_name = 'scheduled_events') 
    is_active = models.BooleanField(blank=True, default=True) 

    def save(self, force_insert=False, force_update=False, using=None): 
     """Overridden to create events the first time.""" 

     self.full_clean() 
     #First do normal save so the data is there for the even scheduler. 
     self.save_base(force_insert=force_insert, force_update=force_update, using=using) 

     #If nothing is scheduled yet, schedule the first batch 
     if self.scheduled_events.count() == 0 and self.past_event_count == 0: 
      self.scheduleEvents() 


    def clean(self): 
     #repeat on weekly 
     if self.repeat_every < 1: 
      raise ValidationError('Repeat every must be at least 1.') 


     #weekly 
     if self.repeats == EVENT_REPEAT_CHOICES[1][0]: 
     #look for missing stuff 
     if not self.repeat_on: 
      raise ValidationError('Missing repeat on.') 

は最後に、私は、これは、それはこのようになりますうまく動作チェックユニットテストを持っています。 ValueError:多対多リレーションシップを使用するには、 'Recurrence'インスタンスにプライマリキー値が必要です。

cleanメソッドでself.repeat_onを指定すると、エラーが発生します。

私はrepeat_onをオプションにしたいと思っていますが、いくつかの種類の再発が必要です。どのように私はこの作品を作るのですか?それが失敗する原因は何ですか?

答えて

4

Many2Many関係を割り当てる前にrecurrence.save()に電話する必要があります。あなたのコードでは、最初に再発を保存せずに

recurrence.repeat_on = (EVENT_DAY_CHOICES[1][0], EVENT_DAY_CHOICES[3][0], EVENT_DAY_CHOICES[5][0]) 

を実行しないでください。保存されていないため、再帰にはプライマリキーがまだ生成されていないため、Django ORMはM2Mテーブルに外部キーとして何を挿入するかを知らない。

+0

私はまだこれを試してみませんが、私には1つの質問があります。再発呼び出しをクリーンに保存すると、エラーが発生しました。また、このエラーが発生していない場合でも、クリーンアップによって検証エラーが発生します。 repeat_on値が必要な場合もあります。 repeat_onが必要でないときにそれを破ることなくそれらを検証するにはどうすればよいですか? saveへの最初の呼び出しは、常にそれを検証しないでしょうか? – Jon

+0

あなたは正しいですが、これはDjangoのM2M関係の制限です。両方のモデルANDフォームには独自のクリーンメソッドがあることに注意してください。モデルの「クリーン」は自給自足で、そのモデルに載っているデータだけを検証する必要があります。 Many2ManyFieldに 'through'を使用して独自のM2Mテーブルを実装し、中間モデルに' clean'を追加するか、自分で外部キーを保存するか、もっと良くフィットすると思うものはフォームのクリーンアップを使ってリクエスト全体を最初に検証します。 – astevanovic