Django数据库查询意外动作

时间:2015-07-17 16:24:26

标签: python django django-models

我有一个包含这些模型的网站:

class TrainingSession(models.Model):
    user = models.ForeignKey(user)
    active_exercise = models.ForeignKey(Exercise)

class Passage(models.Model):
    text = models.TextField()

class Exercise(models.Model):
    training_session = models.ForeignKey(TrainingSession)
    passage = models.ForeignKey(Passage)

为用户分配了TrainingSession,用于生成段落练习。

网站的一般想法是让用户阅读练习。但是我希望通过用户在当前会话期间尚未阅读的段落生成新的练习。所以为此,TrainingSession类有这个方法:

# this passes
def generate_exercise(self):
   passages = Passage.objects.exclude(exercise__trainingsession=self)
   if not passages:
       raise ObjectDoesNotExist()
   passage = random.choice(passages)
   new_exercise = Exercise.objects.create(training_session=self,
                                          passage=passage)
   self.active_exercise = new_exercise

因此,如果我在创建任何段落之前尝试生成练习,那么您需要提出ObjectDoesNotExist。而这正是本次测试中发生的事情:

# this test passes
def test_no_passages(self):
    Passage.objects.all().delete()
    user = User.objects.create()
    session = TrainingSession.objects.create(user=user)
    with self.assertRaises(ObjectDoesNotExist):
        session.generate_exercise()

如果我有一个段落,并产生练习,它就有效:

# this test passes
def test_generate_one(self):
    Passage.objects.all().delete()
    user = User.objects.create()
    session = TrainingSession.objects.create(user=user)
    Passage.objects.create(passage_title='a', passage_text='b')
    session.generate_exercise()

如果我有一个段落,并尝试连续生成两个练习,它会引发异常,正如我所希望的那样:

# this test passes
def test_cant_generate_second(self):
    Passage.objects.all().delete()
    user = User.objects.create()
    session = TrainingSession.objects.create(user=user)
    Passage.objects.create(passage_title='a', passage_text='b')
    session.generate_exercise()
    with self.assertRaises(ObjectDoesNotExist):
        session.generate_exercise()

但是如果我有两个段落,并尝试生成三个练习,则不会引发错误,并且此后续测试失败:

# never raises the exception and fails
def test_cant_generate_third(self):
    Passage.objects.all().delete()
    user = User.objects.create()
    session = TrainingSession.objects.create(user=user)
    Passage.objects.create(passage_title='a', passage_text='b')
    Passage.objects.create(passage_title='c', passage_text='d')
    session.generate_exercise()
    session.generate_exercise()
    with self.assertRaises(ObjectDoesNotExist):
        session.generate_exercise()

如果我打印出应该使用的'通道:

print 'used:', [ex.passage for ex in
    Exercise.objects.filter(trainingsession=session)]

在最后一次测试中的不同点,发生了一些奇怪的事情。当我还没有产生任何练习时,它一开始是空的。在我生成第一个练习之后,它打印出一个数组,其中第一个生成的练习附加了这个练习。但是当我进行第二次练习时,它只能显示最新的练习和通行证。所以不知何故,

Exercise.objects.filter(trainingsession=session)

只找到最新创建的练习,而不是我创建的指向指定训练课程的所有练习。

所以我的问题是:可能有这样一条原因:

new_exercise = Exercise.objects.create(training_session=self,
                                       passage=passage)

可能导致以后的查询:

passages = Passage.objects.exclude(exercise__trainingsession=self)
是不是行为不端?

我已经尝试过:

new_exercise.save() # --doesn't help

或者,如果这个问题没有明显的答案,我的跟进:你能看出我做错了吗?

1 个答案:

答案 0 :(得分:0)

想出来:

passages = Passage.objects.exclude(exercise__trainingsession=self)

应该是

passages = Passage.objects.exclude(exercise__training_session=self)

使用

exercise__trainingsession

正在遵循TrainingSession到ExerciseSession的active_exercise外键的反向路径,因此只排除了“主动”练习。