【问题标题】:Django Many To Many Complaining about PrimaryKeyDjango 多对多抱怨 PrimaryKey
【发布时间】:2012-03-17 13:47:39
【问题描述】:

我有三个相关的模型。第一个称为 DayOfWeek,它只是一个日期标签和数字。它看起来像这样:

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

  def __str__(self):
    return self.label

每次我同步数据库时,都会使用一个夹具填充这个类。接下来,我有一个事件模型,它看起来像这样:

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 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.')

最后,我有一个单元测试来检查它是否正常,看起来像这样:

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)

只要运行测试,它就会失败,但有以下例外。 ValueError: 'Recurrence' 实例需要有一个主键值才能使用多对多关系。

如果self.repeat_on在clean方法中就发生错误。

我希望 repeat_on 是可选的,只有某些类型的重复需要它。我该如何进行这项工作?我错过了什么导致它失败?

【问题讨论】:

    标签: python django django-models many-to-many


    【解决方案1】:

    在分配 Many2Many 关系之前,您需要调用 recurrence.save()。在你的代码中你做

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

    没有先保存重复。因为它没有保存,recurrence 还没有生成主键,Django ORM 不知道将什么作为外键插入到 M2M 表中。

    【讨论】:

    • 我还没在家尝试这个,但我确实有一个问题。保存重复调用干净,这会导致错误。此外,即使它没有导致此错误,也会导致 clean 引发验证错误。在某些情况下,需要 repeat_on 值。当不需要 repeat_on 时,如何验证它们而不破坏它们?第一次调用 save 总是无法验证,不是吗?
    • 你是对的,这是 Django 中 M2M 关系的限制。请注意,模型和表单都有自己的干净方法。模型的clean 应该是自给自足的,并且仅验证基于该模型的数据。您可以在 Many2ManyField 上使用 through 实现自己的 M2M 表,并在中间模型上添加 clean 并自己保存外键,或者我认为更适合您的方法,首先使用 Form 的 clean 来验证整个请求。
    猜你喜欢
    • 2015-11-30
    • 1970-01-01
    • 2022-12-13
    • 2013-10-25
    • 1970-01-01
    • 2011-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多