launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #12549
[Merge] lp:~jameinel/maas/tag-auto-populate into lp:maas
John A Meinel has proposed merging lp:~jameinel/maas/tag-auto-populate into lp:maas.
Commit message:
Change Tag.save() to call self.populate_nodes() if the definition changed.
This makes it much easier to DTRT when using Tag, rather than having to think 'ok, did I change something that needs to rebuild?'.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~jameinel/maas/tag-auto-populate/+merge/126603
This makes Tag call self.populate_nodes during save(). Should simplify things a bit for using Tag.
I followed the advice here: http://stackoverflow.com/questions/1355150/django-when-saving-how-can-you-check-if-a-field-has-changed
I almost wonder if this should be done in a 'clean' method rather than a 'save' method. Though on initial creation, you have to do it after save, because otherwise you don't have an ID for foreign-key references.
--
https://code.launchpad.net/~jameinel/maas/tag-auto-populate/+merge/126603
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jameinel/maas/tag-auto-populate into lp:maas.
=== modified file 'src/maasserver/api.py'
--- src/maasserver/api.py 2012-09-26 10:00:14 +0000
+++ src/maasserver/api.py 2012-09-27 05:51:21 +0000
@@ -1280,8 +1280,6 @@
try:
new_tag = form.save(commit=False)
new_tag.save()
- if new_tag.definition != old_definition:
- new_tag.populate_nodes()
form.save_m2m()
except DatabaseError as e:
raise ValidationError(e)
@@ -1348,11 +1346,7 @@
raise PermissionDenied()
form = TagForm(request.data)
if form.is_valid():
- new_tag = form.save(commit=False)
- new_tag.save()
- new_tag.populate_nodes()
- form.save_m2m()
- return new_tag
+ return form.save()
else:
raise ValidationError(form.errors)
=== modified file 'src/maasserver/models/tag.py'
--- src/maasserver/models/tag.py 2012-09-26 10:00:14 +0000
+++ src/maasserver/models/tag.py 2012-09-27 05:51:21 +0000
@@ -94,6 +94,17 @@
objects = TagManager()
+ def __init__(self, *args, **kwargs):
+ super(Tag, self).__init__(*args, **kwargs)
+ # Track what the original definition is, so we can detect when it
+ # changes and we need to repopulate the node<=>tag mapping.
+ # We have to check for self.id, otherwise we don't see the creation of
+ # a new definition.
+ if self.id is None:
+ self._original_definition = None
+ else:
+ self._original_definition = self.definition
+
def __unicode__(self):
return self.name
@@ -111,3 +122,9 @@
# Is there an efficiency difference between doing
# 'node.tags.add(self)' and 'self.node_set.add(node)' ?
node.tags.add(self)
+
+ def save(self, *args, **kwargs):
+ super(Tag, self).save(*args, **kwargs)
+ if self.definition != self._original_definition:
+ self.populate_nodes()
+ self._original_definition = self.definition
=== modified file 'src/maasserver/tests/test_tag.py'
--- src/maasserver/tests/test_tag.py 2012-09-26 12:26:54 +0000
+++ src/maasserver/tests/test_tag.py 2012-09-27 05:51:21 +0000
@@ -43,41 +43,42 @@
self.assertEqual([tag.id], [t.id for t in node.tags.all()])
self.assertEqual([node.id], [n.id for n in tag.node_set.all()])
- def test_populate_nodes_applies_tags_to_nodes(self):
+ def test_applies_tags_to_nodes(self):
node1 = factory.make_node()
node1.set_hardware_details('<node><child /></node>')
node2 = factory.make_node()
node2.set_hardware_details('<node />')
tag = factory.make_tag(definition='/node/child')
- tag.populate_nodes()
self.assertItemsEqual([tag.name], node1.tag_names())
self.assertItemsEqual([], node2.tag_names())
- def test_populate_nodes_removes_old_values(self):
+ def test_removes_old_values(self):
node1 = factory.make_node()
node1.set_hardware_details('<node><foo /></node>')
node2 = factory.make_node()
node2.set_hardware_details('<node><bar /></node>')
tag = factory.make_tag(definition='/node/foo')
- tag.populate_nodes()
self.assertItemsEqual([tag.name], node1.tag_names())
self.assertItemsEqual([], node2.tag_names())
tag.definition = '/node/bar'
- tag.populate_nodes()
+ tag.save()
self.assertItemsEqual([], node1.tag_names())
self.assertItemsEqual([tag.name], node2.tag_names())
+ # And we notice if we change it *again* and then save.
+ tag.definition = '/node/foo'
+ tag.save()
+ self.assertItemsEqual([tag.name], node1.tag_names())
+ self.assertItemsEqual([], node2.tag_names())
- def test_populate_nodes_doesnt_touch_other_tags(self):
+ def test_doesnt_touch_other_tags(self):
node1 = factory.make_node()
node1.set_hardware_details('<node><foo /></node>')
node2 = factory.make_node()
node2.set_hardware_details('<node><bar /></node>')
tag1 = factory.make_tag(definition='/node/foo')
- tag1.populate_nodes()
self.assertItemsEqual([tag1.name], node1.tag_names())
self.assertItemsEqual([], node2.tag_names())
tag2 = factory.make_tag(definition='/node/bar')
- tag2.populate_nodes()
self.assertItemsEqual([tag1.name], node1.tag_names())
self.assertItemsEqual([tag2.name], node2.tag_names())
@@ -118,13 +119,12 @@
class TestTagTransactions(TransactionTestCase):
- def test_populate_nodes_rollsback_invalid_xpath(self):
+ def test_rollsback_invalid_xpath(self):
@transaction.commit_manually
def setup():
node = factory.make_node()
node.set_hardware_details('<node><foo /></node>')
tag = factory.make_tag(definition='/node/foo')
- tag.populate_nodes()
self.assertItemsEqual([tag.name], node.tag_names())
transaction.commit()
return tag, node
@@ -132,7 +132,7 @@
@transaction.commit_manually
def trigger_invalid():
tag.definition = 'invalid::tag'
- self.assertRaises(DatabaseError, tag.populate_nodes)
+ self.assertRaises(DatabaseError, tag.save)
transaction.rollback()
# Because the definition is invalid, the db should not have been
# updated