diff options
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | semantic_version/django_fields.py | 27 | ||||
-rw-r--r-- | tests/test_django.py | 89 |
4 files changed, 83 insertions, 42 deletions
@@ -2,9 +2,14 @@ ChangeLog ========= -2.5.1 (master) +2.6.0 (master) -------------- +*New:* + + * `#43 <https://github.com/rbarrois/python-semanticversion/issues/43>`_: + Add support for Django up to 1.10. + *Bugfix:* * `#35 <https://github.com/rbarrois/python-semanticversion/issues/35>`_: @@ -6,7 +6,7 @@ DOC_DIR=docs COVERAGE = python $(shell which coverage) # Dependencies -DJANGO_VERSION ?= 1.9 +DJANGO_VERSION ?= 1.10 PYTHON_VERSION := $(shell python --version) NEXT_DJANGO_VERSION=$(shell python -c "v='$(DJANGO_VERSION)'; parts=v.split('.'); parts[-1]=str(int(parts[-1])+1); print('.'.join(parts))") diff --git a/semantic_version/django_fields.py b/semantic_version/django_fields.py index eba8658..c50297e 100644 --- a/semantic_version/django_fields.py +++ b/semantic_version/django_fields.py @@ -4,18 +4,32 @@ from __future__ import unicode_literals +import django from django.db import models from django.utils.translation import ugettext_lazy as _ from . import base -class BaseSemVerField(models.CharField): - __metaclass__ = models.SubfieldBase +class SemVerField(models.CharField): def __init__(self, *args, **kwargs): kwargs.setdefault('max_length', 200) - super(BaseSemVerField, self).__init__(*args, **kwargs) + super(SemVerField, self).__init__(*args, **kwargs) + + if django.VERSION[:2] < (1, 8): + def contribute_to_class(self, cls, name, **kwargs): + """Emulate SubFieldBase for Django < 1.8""" + super(SemVerField, self).contribute_to_class(cls, name, **kwargs) + from django.db.models.fields import subclassing + setattr(cls, self.name, subclassing.Creator(self)) + + def from_db_value(self, value, expression, connection, context): + """Convert from the database format. + + This should be the inverse of self.get_prep_value() + """ + return self.to_python(value) def get_prep_value(self, obj): return None if obj is None else str(obj) @@ -30,12 +44,7 @@ class BaseSemVerField(models.CharField): return str(value) def run_validators(self, value): - return super(BaseSemVerField, self).run_validators(str(value)) - - -# Py2 and Py3-compatible metaclass -SemVerField = models.SubfieldBase( - str('SemVerField'), (BaseSemVerField, models.CharField), {}) + return super(SemVerField, self).run_validators(str(value)) class VersionField(SemVerField): diff --git a/tests/test_django.py b/tests/test_django.py index c93dde7..478c657 100644 --- a/tests/test_django.py +++ b/tests/test_django.py @@ -38,23 +38,33 @@ def save_and_refresh(obj): obj = obj.__class__.objects.get(id=obj.id) +Version = semantic_version.Version +Spec = semantic_version.Spec + + @unittest.skipIf(not django_loaded, "Django not installed") class DjangoFieldTestCase(unittest.TestCase): def test_version(self): - obj = models.VersionModel(version='0.1.1', spec='==0.1.1,!=0.1.1-alpha') + obj = models.VersionModel(version=Version('0.1.1'), spec=Spec('==0.1.1,!=0.1.1-alpha')) - self.assertEqual(semantic_version.Version('0.1.1'), obj.version) - self.assertEqual(semantic_version.Spec('==0.1.1,!=0.1.1-alpha'), obj.spec) + self.assertEqual(Version('0.1.1'), obj.version) + self.assertEqual(Spec('==0.1.1,!=0.1.1-alpha'), obj.spec) alt_obj = models.VersionModel(version=obj.version, spec=obj.spec) - self.assertEqual(semantic_version.Version('0.1.1'), alt_obj.version) - self.assertEqual(semantic_version.Spec('==0.1.1,!=0.1.1-alpha'), alt_obj.spec) + self.assertEqual(Version('0.1.1'), alt_obj.version) + self.assertEqual(Spec('==0.1.1,!=0.1.1-alpha'), alt_obj.spec) self.assertEqual(obj.spec, alt_obj.spec) self.assertEqual(obj.version, alt_obj.version) + def test_version_clean(self): + """Calling .full_clean() should convert str to Version/Spec objects.""" + obj = models.VersionModel(version='0.1.1', spec='==0.1.1,!=0.1.1-alpha') obj.full_clean() + self.assertEqual(Version('0.1.1'), obj.version) + self.assertEqual(Spec('==0.1.1,!=0.1.1-alpha'), obj.spec) + def test_version_save(self): """Test saving object with a VersionField.""" # first test with a null value @@ -66,7 +76,7 @@ class DjangoFieldTestCase(unittest.TestCase): self.assertIsNone(obj.optional_spec) # now set to something that is not null - spec = semantic_version.Spec('==0,!=0.2') + spec = Spec('==0,!=0.2') obj.optional_spec = spec save_and_refresh(obj) self.assertEqual(obj.optional_spec, spec) @@ -82,35 +92,45 @@ class DjangoFieldTestCase(unittest.TestCase): self.assertIsNone(obj.optional_spec) # now set to something that is not null - spec = semantic_version.Spec('==0,!=0.2') + spec = Spec('==0,!=0.2') obj.optional_spec = spec save_and_refresh(obj) self.assertEqual(obj.optional_spec, spec) - def test_partial_spec(self): + def test_partial_spec_clean(self): obj = models.VersionModel(version='0.1.1', spec='==0,!=0.2') - self.assertEqual(semantic_version.Version('0.1.1'), obj.version) - self.assertEqual(semantic_version.Spec('==0,!=0.2'), obj.spec) + obj.full_clean() + self.assertEqual(Version('0.1.1'), obj.version) + self.assertEqual(Spec('==0,!=0.2'), obj.spec) - def test_coerce(self): + def test_coerce_clean(self): obj = models.CoerceVersionModel(version='0.1.1a+2', partial='23') - self.assertEqual(semantic_version.Version('0.1.1-a+2'), obj.version) - self.assertEqual(semantic_version.Version('23', partial=True), obj.partial) + obj.full_clean() + self.assertEqual(Version('0.1.1-a+2'), obj.version) + self.assertEqual(Version('23', partial=True), obj.partial) obj2 = models.CoerceVersionModel(version='23', partial='0.1.2.3.4.5/6') - self.assertEqual(semantic_version.Version('23.0.0'), obj2.version) - self.assertEqual(semantic_version.Version('0.1.2+3.4.5-6', partial=True), obj2.partial) + obj2.full_clean() + self.assertEqual(Version('23.0.0'), obj2.version) + self.assertEqual(Version('0.1.2+3.4.5-6', partial=True), obj2.partial) + @unittest.skipIf(django.VERSION[:2] < (1, 8), "Django<1.8 casts values on setattr") def test_invalid_input(self): - self.assertRaises(ValueError, models.VersionModel, - version='0.1.1', spec='blah') - self.assertRaises(ValueError, models.VersionModel, - version='0.1', spec='==0.1.1,!=0.1.1-alpha') + v = models.VersionModel(version='0.1.1', spec='blah') + self.assertRaises(ValueError, v.full_clean) + + v2 = models.VersionModel(version='0.1', spec='==0.1.1,!=0.1.1-alpha') + self.assertRaises(ValueError, v2.full_clean) + + @unittest.skipUnless(django.VERSION[:2] < (1, 8), "Django>=1.8 doesn't mangle setattr") + def test_invalid_input_full_clean(self): + self.assertRaises(ValueError, models.VersionModel, version='0.1.1', spec='blah') + self.assertRaises(ValueError, models.VersionModel, version='0.1', spec='==0.1.1,!=0.1.1-alpha') def test_partial(self): - obj = models.PartialVersionModel(partial='0.1.0') + obj = models.PartialVersionModel(partial=Version('0.1.0')) - self.assertEqual(semantic_version.Version('0.1.0', partial=True), obj.partial) + self.assertEqual(Version('0.1.0', partial=True), obj.partial) self.assertIsNone(obj.optional) self.assertIsNone(obj.optional_spec) @@ -121,17 +141,18 @@ class DjangoFieldTestCase(unittest.TestCase): optional_spec=obj.optional_spec, ) - self.assertEqual(semantic_version.Version('0.1.0', partial=True), alt_obj.partial) + self.assertEqual(Version('0.1.0', partial=True), alt_obj.partial) self.assertEqual(obj.partial, alt_obj.partial) self.assertIsNone(obj.optional) self.assertIsNone(obj.optional_spec) + # Validation should be fine obj.full_clean() def test_serialization(self): - o1 = models.VersionModel(version='0.1.1', spec='==0.1.1,!=0.1.1-alpha') - o2 = models.VersionModel(version='0.4.3-rc3+build3', - spec='<=0.1.1-rc2,!=0.1.1-rc1') + o1 = models.VersionModel(version=Version('0.1.1'), spec=Spec('==0.1.1,!=0.1.1-alpha')) + o2 = models.VersionModel(version=Version('0.4.3-rc3+build3'), + spec=Spec('<=0.1.1-rc2,!=0.1.1-rc1')) data = serializers.serialize('json', [o1, o2]) @@ -142,10 +163,16 @@ class DjangoFieldTestCase(unittest.TestCase): self.assertEqual(o2.spec, obj2.object.spec) def test_serialization_partial(self): - o1 = models.PartialVersionModel(partial='0.1.1', optional='0.2.4-rc42', - optional_spec=None) - o2 = models.PartialVersionModel(partial='0.4.3-rc3+build3', optional='', - optional_spec='==0.1.1,!=0.1.1-alpha') + o1 = models.PartialVersionModel( + partial=Version('0.1.1', partial=True), + optional=Version('0.2.4-rc42', partial=True), + optional_spec=None, + ) + o2 = models.PartialVersionModel( + partial=Version('0.4.3-rc3+build3', partial=True), + optional='', + optional_spec=Spec('==0.1.1,!=0.1.1-alpha'), + ) data = serializers.serialize('json', [o1, o2]) @@ -234,8 +261,8 @@ if django_loaded: TestRunner().teardown_databases(cls.old_state) def test_db_interaction(self): - o1 = models.VersionModel(version='0.1.1', spec='<0.2.4-rc42') - o2 = models.VersionModel(version='0.4.3-rc3+build3', spec='==0.4.3') + o1 = models.VersionModel(version=Version('0.1.1'), spec=Spec('<0.2.4-rc42')) + o2 = models.VersionModel(version=Version('0.4.3-rc3+build3'), spec=Spec('==0.4.3')) o1.save() o2.save() |