Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'release/0.3.3'

  • Loading branch information...
commit 6295ca5ed6e7e6518658c66888ae9f5407aac614 2 parents 5bfb3ce + 235f99c
@timdiggins timdiggins authored
View
5 NEWS.txt
@@ -32,3 +32,8 @@ Release notes
---
* Allowing for full inheritance of immutability options within Meta
+
+0.3.3
+---
+
+* Fixing problem where immutable (non undeletable) models with default lock_field couldn't be deleted after save (because of Django deletion collector needing to change id field)
View
7 TODO.txt
@@ -1,2 +1,5 @@
-Inheriting options from parent abstract classes?
-Tests for admin stuff.
+Should we allow lock_fields to be changed after locking
+(ie. allow unlocking? maybe by default not,
+but then have a new config option immutable_unlockable = True)
+
+Tests for admin ui.
View
2  immutablemodel/__init__.py
@@ -6,5 +6,5 @@
Please see LICENSE and AUTHORS for more information.
"""
-from models import ImmutableModel, CantDeleteImmutableException
+from models import ImmutableModel, CantDeleteImmutableException, PK_FIELD, UNDEFINED
from admin import ImmutableModelAdmin
View
13 immutablemodel/models.py
@@ -148,8 +148,13 @@ class ImmutableModel(models.Model):
__metaclass__ = ImmutableModelMeta
def can_change_field(self, field_name):
- return field_name in self._meta.mutable_fields or not self.is_immutable()
-
+ field_changable = field_name in self._meta.mutable_fields or not self.is_immutable()
+ if not field_changable and field_name == self._meta.pk.attname:
+ if getattr(self, '_deleting_immutable_model', False):
+ #deleting this immutable model, so need to allow Collector.delete to change the field
+ return True
+ return field_changable
+
def __setattr__(self, name, value):
if not self.can_change_field(name):
try:
@@ -161,7 +166,7 @@ def __setattr__(self, name, value):
current_value != value:
if self._meta.immutable_quiet:
return
- raise ValueError('%s is immutable and cannot be changed' % name)
+ raise ValueError('%s.%s is immutable and cannot be changed' % (self.__class__.__name__, name))
super(ImmutableModel, self).__setattr__(name, value)
def is_immutable(self):
@@ -188,7 +193,9 @@ def delete(self):
raise CantDeleteImmutableException(
"%s is immutable and cannot be deleted" % self
)
+ self._deleting_immutable_model = True
super(ImmutableModel, self).delete()
+ delattr(self, '_deleting_immutable_model')
class Meta:
abstract = True
View
2  setup.py
@@ -8,7 +8,7 @@
setup(
name = 'django-immutablemodel',
- version = '0.3.2',
+ version = '0.3.3',
description="A base class for Django to allow immutable fields on Models",
long_description=README + '\n\n' + NEWS,
classifiers=[
View
70 tests/test_immutablemodel.py
@@ -31,10 +31,14 @@ def test03_delete(self):
len(NoMeta.objects.all()),
)
+ def test_cant_change_id_field(self):
+ self.obj.id = None
+ self.assertNotEqual(self.obj.id, None, "expecting not to be able to change id field (not automatically locked)")
-class Case02_CanCreateModelNoSignOffFieldTest(TestCase):
+
+class Case02_CanCreateModelNoLockFieldTest(TestCase):
def setUp(self):
- self.obj = SimpleNoSignOffField.objects.create(
+ self.obj = SimpleNoLockField.objects.create(
special_id=1,
name='Vader',
)
@@ -48,7 +52,7 @@ def test__simple(self):
self.obj.save()
- db_object = SimpleNoSignOffField.objects.all()[0]
+ db_object = SimpleNoLockField.objects.all()[0]
# Should stay the same, it's immutable. Except the name.
self.assertEqual(self.obj.special_id, 1)
self.assertEqual(self.obj.name, 'Luke')
@@ -59,8 +63,13 @@ def test__delete(self):
self.obj.delete()
self.assertEqual(
0,
- len(SimpleNoSignOffField.objects.all()),
+ len(SimpleNoLockField.objects.all()),
)
+
+ def test_can_change_id_field(self):
+ self.obj.id = None
+ self.assertEqual(self.obj.id, None, "expecting to be able to change id field (not automatically locked)")
+
class Case03_HavingMutableField_Test(TestCase):
def setUp(self):
self.obj = HavingMutableField.objects.create(
@@ -85,9 +94,9 @@ def test__simple(self):
self.assertEqual(db_object.name, 'Luke')
-class Case03_CanCreateModelSignOffFieldTest(TestCase):
+class Case03_CanCreateModelLockFieldTest(TestCase):
def setUp(self):
- self.obj = SimpleSignOffField.objects.create(
+ self.obj = SimpleLockField.objects.create(
special_id=1,
name='Yoda',
is_locked=False,
@@ -101,7 +110,7 @@ def test__simple_not_locked(self):
self.obj.name = 'Obi-Wan'
self.obj.save()
- db_object = SimpleSignOffField.objects.all()[0]
+ db_object = SimpleLockField.objects.all()[0]
# Should change, since the lock field is false
self.assertEqual(self.obj.special_id, 1337)
@@ -117,7 +126,7 @@ def test__simple_locked(self):
self.obj.name = 'Obi-Wan'
self.obj.save()
- db_object = SimpleSignOffField.objects.all()[0]
+ db_object = SimpleLockField.objects.all()[0]
# Should not change, since the lock field is true
# Of course, that name is still changable
@@ -132,7 +141,7 @@ def test__delete_not_locked(self):
self.obj.delete()
self.assertEqual(
0,
- len(SimpleSignOffField.objects.all()),
+ len(SimpleLockField.objects.all()),
)
def test__delete_locked(self):
@@ -141,13 +150,13 @@ def test__delete_locked(self):
self.obj.delete()
self.assertEqual(
0,
- len(SimpleSignOffField.objects.all()),
+ len(SimpleLockField.objects.all()),
)
-class Case04_CanCreateModelSignOffFieldInAnyOrderTest(TestCase):
+class Case04_CanCreateModelLockFieldInAnyOrderTest(TestCase):
def setUp(self):
- self.obj = ComplexSignOffField.objects.create(
+ self.obj = ComplexLockField.objects.create(
is_locked=False,
special_id=1,
name='Yoda',
@@ -161,7 +170,7 @@ def test__simple_not_locked(self):
self.obj.name = 'Obi-Wan'
self.obj.save()
- db_object = ComplexSignOffField.objects.all()[0]
+ db_object = ComplexLockField.objects.all()[0]
# Should change, since the lock field is false
self.assertEqual(self.obj.special_id, 1337)
@@ -177,7 +186,7 @@ def test__simple_locked(self):
self.obj.name = 'Obi-Wan'
self.obj.save()
- db_object = ComplexSignOffField.objects.all()[0]
+ db_object = ComplexLockField.objects.all()[0]
# Should not change, since the lock field is true
# Of course, that name is still changable
@@ -189,7 +198,7 @@ def test__simple_locked(self):
self.assertEqual(db_object.is_locked, True)
def test__lock_field_true_at_create(self):
- is_locked_at_first = ComplexSignOffField.objects.create(
+ is_locked_at_first = ComplexLockField.objects.create(
is_locked=True,
special_id=100,
name='Yoda',
@@ -214,7 +223,7 @@ def test__lock_field_true_at_create(self):
is_locked_at_first.name = 'Obi-Wan'
is_locked_at_first.save()
- db_object = ComplexSignOffField.objects.get(special_id=100)
+ db_object = ComplexLockField.objects.get(special_id=100)
# Should not change, since the lock field is true
# Of course, that name is still changable
@@ -229,7 +238,7 @@ def test__delete_not_locked(self):
self.obj.delete()
self.assertEqual(
0,
- len(ComplexSignOffField.objects.all()),
+ len(ComplexLockField.objects.all()),
)
def test__delete_locked(self):
@@ -238,7 +247,7 @@ def test__delete_locked(self):
self.obj.delete()
self.assertEqual(
0,
- len(ComplexSignOffField.objects.all()),
+ len(ComplexLockField.objects.all()),
)
class Case05_QuietCannotDelete(TestCase):
@@ -256,15 +265,16 @@ def test_no_delete(self):
class Case06_WillRaiseErrorsTest(TestCase):
def setUp(self):
- self.no_lock_field = NoisyNoSignOffField.objects.create(
+ self.no_lock_field = NoisyNoLockField.objects.create(
special_id=1,
)
- self.lock_field = NoisySignOffField.objects.create(
+ self.lock_field = NoisyLockField.objects.create(
special_id=5,
)
self.not_deletable_noisy = NoisyNotDeletable.objects.create(
special_id=1123,
)
+ self.noisy_minimal = NoisyMinimal.objects.create(special_id=1123)
def test__simple(self):
self.assertEqual(self.no_lock_field.special_id, 1)
@@ -287,7 +297,7 @@ def test__simple(self):
)
def test__lock_field_true_at_create(self):
- is_locked_at_first = NoisySignOffField.objects.create(
+ is_locked_at_first = NoisyLockField.objects.create(
is_locked=True,
special_id=1,
)
@@ -318,11 +328,11 @@ def test__delete_not_locked(self):
)
self.assertEqual(
0,
- len(NoisyNoSignOffField.objects.all()),
+ len(NoisyNoLockField.objects.all()),
)
self.assertEqual(
0,
- len(NoisySignOffField.objects.all()),
+ len(NoisyLockField.objects.all()),
)
def test__delete_locked(self):
@@ -331,9 +341,21 @@ def test__delete_locked(self):
self.lock_field.delete()
self.assertEqual(
0,
- len(NoisySignOffField.objects.all()),
+ len(NoisyLockField.objects.all()),
)
+ def test_can_delete_deletable_locked(self):
+ for name in [
+ 'no_lock_field',
+ 'lock_field',
+ 'noisy_minimal',
+ ]:
+ instance = getattr(self, name)
+ instance_id = instance.id
+ model = instance.__class__
+ instance.delete()
+ self.assertFalse(model.objects.filter(pk=instance_id).exists(), 'not expecting %s to exist after delete' % name)
+
class Case07_InheritenceTests(TestCase):
def test01_defaults_work_for_abstract(self):
View
23 tests/testapp/models.py
@@ -13,15 +13,15 @@ class Meta:
mutable_fields = ['name']
-class SimpleNoSignOffField(ImmutableModel):
+class SimpleNoLockField(ImmutableModel):
special_id = models.IntegerField()
name = models.CharField(max_length=50)
class Meta:
immutable_fields = ['special_id']
-
-
-class SimpleSignOffField(ImmutableModel):
+ immutable_lock_field = None
+
+class SimpleLockField(ImmutableModel):
special_id = models.IntegerField()
name = models.CharField(max_length=50)
is_locked = models.BooleanField(default=False)
@@ -31,7 +31,7 @@ class Meta:
immutable_lock_field = 'is_locked'
-class ComplexSignOffField(ImmutableModel):
+class ComplexLockField(ImmutableModel):
is_locked = models.BooleanField(default=True)
special_id = models.IntegerField()
name = models.CharField(max_length=50)
@@ -41,15 +41,22 @@ class Meta:
immutable_lock_field = 'is_locked'
-class NoisyNoSignOffField(ImmutableModel):
+class NoisyMinimal(ImmutableModel):
special_id = models.IntegerField()
class Meta:
- immutable_fields = ['special_id']
immutable_quiet = False
+
+class NoisyNoLockField(ImmutableModel):
+ special_id = models.IntegerField()
+ class Meta:
+ immutable_fields = ['special_id']
+ immutable_quiet = False
+ immutable_lock_field = None
+
-class NoisySignOffField(ImmutableModel):
+class NoisyLockField(ImmutableModel):
special_id = models.IntegerField()
is_locked = models.BooleanField(default=False)
Please sign in to comment.
Something went wrong with that request. Please try again.