From 770f402905ba0cbcc818d0b97853e16204e7efc8 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 17 Nov 2011 23:09:18 +0900 Subject: [PATCH] don't allow template removal to orphan facts --- anki/models.py | 15 ++++++++++++++- tests/test_models.py | 4 +++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/anki/models.py b/anki/models.py index 06060fa4a..b8675cadd 100644 --- a/anki/models.py +++ b/anki/models.py @@ -345,11 +345,23 @@ select id from cards where fid in (select id from facts where mid = ?)""", self.save(m) def remTemplate(self, m, template): - self.deck.modSchema() + "False if removing template would leave orphan facts." + # find cards using this template ord = m['tmpls'].index(template) cids = self.deck.db.list(""" select c.id from cards c, facts f where c.fid=f.id and mid = ? and ord = ?""", m['id'], ord) + # all facts with this template must have at least two cards, or we + # could end up creating orphaned facts + if self.deck.db.scalar(""" +select fid, count() from cards where +fid in (select fid from cards where id in %s) +group by fid +having count() < 2 +limit 1""" % ids2str(cids)): + return False + # ok to proceed; remove cards + self.deck.modSchema() self.deck.remCards(cids) # shift ordinals self.deck.db.execute(""" @@ -359,6 +371,7 @@ update cards set ord = ord - 1, usn = ?, mod = ? m['tmpls'].remove(template) self._updateTemplOrds(m) self.save(m) + return True def _updateTemplOrds(self, m): for c, t in enumerate(m['tmpls']): diff --git a/tests/test_models.py b/tests/test_models.py index 655fac32c..fbbbff4c0 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -96,12 +96,14 @@ def test_templates(): assert c.ord == 1 assert c2.ord == 0 # removing a template should delete its cards - d.models.remTemplate(m, m['tmpls'][0]) + assert d.models.remTemplate(m, m['tmpls'][0]) assert d.cardCount() == 1 # and should have updated the other cards' ordinals c = f.cards()[0] assert c.ord == 0 stripHTML(c.q()) == "2" + # it shouldn't be possible to orphan facts by removing templates + assert not d.models.remTemplate(m, m['tmpls'][0]) def test_text(): d = getEmptyDeck()