Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove unsupported django version hacks. #390

Merged
merged 2 commits into from
Aug 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
CHANGES
=======

3.4.0 (unreleased)
------------------

- Remove hacks for previously supported Django versions. (Fixes GH-390)

3.3.0 (2019.08.19)
------------------

- Added `Choices.subset`.

3.2.0 (2019.06.21)
Expand Down
13 changes: 4 additions & 9 deletions model_utils/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,7 @@ def prepare_class(self, sender, **kwargs):
assert hasattr(sender, self.choices_name), \
"To use StatusField, the model '%s' must have a %s choices class attribute." \
% (sender.__name__, self.choices_name)
self._choices = getattr(sender, self.choices_name)
if django.VERSION >= (1, 9, 0):
self.choices = self._choices
self.choices = getattr(sender, self.choices_name)
if not self.has_default():
self.default = tuple(getattr(sender, self.choices_name))[0][0] # set first as default

Expand All @@ -92,9 +90,7 @@ def contribute_to_class(self, cls, name):
# we don't set the real choices until class_prepared (so we can rely on
# the STATUS class attr being available), but we need to set some dummy
# choices now so the super method will add the get_FOO_display method
self._choices = [(0, 'dummy')]
if django.VERSION >= (1, 9, 0):
self.choices = self._choices
self.choices = [(0, 'dummy')]
super(StatusField, self).contribute_to_class(cls, name)

def deconstruct(self):
Expand Down Expand Up @@ -133,11 +129,10 @@ def get_monitored_value(self, instance):
return getattr(instance, self.monitor)

def _save_initial(self, sender, instance, **kwargs):
if django.VERSION >= (1, 10) and self.monitor in instance.get_deferred_fields():
if self.monitor in instance.get_deferred_fields():
# Fix related to issue #241 to avoid recursive error on double monitor fields
return
setattr(instance, self.monitor_attname,
self.get_monitored_value(instance))
setattr(instance, self.monitor_attname, self.get_monitored_value(instance))

def pre_save(self, model_instance, add):
value = now()
Expand Down
34 changes: 0 additions & 34 deletions model_utils/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,35 +114,6 @@ def annotate(self, *args, **kwargs):
qset._annotated = [a.default_alias for a in args] + list(kwargs.keys())
return qset

def iterator(self):
# Maintained for Django 1.8 compatability
iter = super(InheritanceQuerySetMixin, self).iterator()
if getattr(self, 'subclasses', False):
extras = tuple(self.query.extra.keys())
# sort the subclass names longest first,
# so with 'a' and 'a__b' it goes as deep as possible
subclasses = sorted(self.subclasses, key=len, reverse=True)
for obj in iter:
sub_obj = None
for s in subclasses:
sub_obj = self._get_sub_obj_recurse(obj, s)
if sub_obj:
break
if not sub_obj:
sub_obj = obj

if getattr(self, '_annotated', False):
for k in self._annotated:
setattr(sub_obj, k, getattr(obj, k))

for k in extras:
setattr(sub_obj, k, getattr(obj, k))

yield sub_obj
else:
for obj in iter:
yield obj

def _get_subclasses_recurse(self, model, levels=None):
"""
Given a Model class, find all related objects, exploring children
Expand Down Expand Up @@ -202,11 +173,6 @@ def _get_ancestors_path(self, model, levels=None):
def _get_sub_obj_recurse(self, obj, s):
rel, _, s = s.partition(LOOKUP_SEP)

# Django 1.9: If a primitive type gets passed to this recursive function,
# return None as non-models are not part of inheritance.
if not isinstance(obj, models.Model):
return None

try:
node = getattr(obj, rel)
except ObjectDoesNotExist:
Expand Down
16 changes: 4 additions & 12 deletions model_utils/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import unicode_literals

import django
from django.core.exceptions import ImproperlyConfigured
from django.db import models, transaction, router
from django.db.models.signals import post_save, pre_save
Expand All @@ -18,11 +17,8 @@
SoftDeletableManager,
)

if django.VERSION >= (1, 9, 0):
from django.db.models.functions import Now
now = Now()
else:
from django.utils.timezone import now
from django.db.models.functions import Now
now = Now()


class TimeStampedModel(models.Model):
Expand Down Expand Up @@ -75,9 +71,7 @@ def add_status_query_managers(sender, **kwargs):
if not issubclass(sender, StatusModel):
return

if django.VERSION >= (1, 10):
# First, get current manager name...
default_manager = sender._meta.default_manager
default_manager = sender._meta.default_manager

for value, display in getattr(sender, 'STATUS', ()):
if _field_exists(sender, value):
Expand All @@ -88,9 +82,7 @@ def add_status_query_managers(sender, **kwargs):
)
sender.add_to_class(value, QueryManager(status=value))

if django.VERSION >= (1, 10):
# ...then, put it back, as add_to_class is modifying the default manager!
sender._meta.default_manager_name = default_manager.name
sender._meta.default_manager_name = default_manager.name


def add_timeframed_query_manager(sender, **kwargs):
Expand Down
2 changes: 0 additions & 2 deletions model_utils/tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,6 @@ def __init__(self, instance, fields, field_map):
self.instance = instance
self.fields = fields
self.field_map = field_map
if django.VERSION < (1, 10):
self.init_deferred_fields()

@property
def deferred_fields(self):
Expand Down
29 changes: 10 additions & 19 deletions tests/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,13 @@ def mutable_to_db(value):
return str(value)


if django.VERSION >= (1, 9, 0):
class MutableField(models.TextField):
def to_python(self, value):
return mutable_from_db(value)

def from_db_value(self, value, expression, connection, context):
return mutable_from_db(value)

def get_db_prep_save(self, value, connection):
value = super(MutableField, self).get_db_prep_save(value, connection)
return mutable_to_db(value)
else:
class MutableField(with_metaclass(models.SubfieldBase, models.TextField)):
def to_python(self, value):
return mutable_from_db(value)

def get_db_prep_save(self, value, connection):
value = mutable_to_db(value)
return super(MutableField, self).get_db_prep_save(value, connection)
class MutableField(models.TextField):
def to_python(self, value):
return mutable_from_db(value)

def from_db_value(self, value, expression, connection, context):
return mutable_from_db(value)

def get_db_prep_save(self, value, connection):
value = super(MutableField, self).get_db_prep_save(value, connection)
return mutable_to_db(value)
22 changes: 5 additions & 17 deletions tests/test_fields/test_field_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,35 +185,23 @@ def test_with_deferred(self):
self.instance.number = 1
self.instance.save()
item = self.tracked_class.objects.only('name').first()
if django.VERSION >= (1, 10):
self.assertTrue(item.get_deferred_fields())
else:
self.assertTrue(item._deferred_fields)
self.assertTrue(item.get_deferred_fields())

# has_changed() returns False for deferred fields, without un-deferring them.
# Use an if because ModelTracked doesn't support has_changed() in this case.
if self.tracked_class == Tracked:
self.assertFalse(item.tracker.has_changed('number'))
if django.VERSION >= (1, 10):
self.assertIsInstance(item.__class__.number, DescriptorWrapper)
self.assertTrue('number' in item.get_deferred_fields())
else:
self.assertTrue('number' in item._deferred_fields)
self.assertIsInstance(item.__class__.number, DescriptorWrapper)
self.assertTrue('number' in item.get_deferred_fields())

# previous() un-defers field and returns value
self.assertEqual(item.tracker.previous('number'), 1)
if django.VERSION >= (1, 10):
self.assertNotIn('number', item.get_deferred_fields())
else:
self.assertNotIn('number', item._deferred_fields)
self.assertNotIn('number', item.get_deferred_fields())

# examining a deferred field un-defers it
item = self.tracked_class.objects.only('name').first()
self.assertEqual(item.number, 1)
if django.VERSION >= (1, 10):
self.assertTrue('number' not in item.get_deferred_fields())
else:
self.assertTrue('number' not in item._deferred_fields)
self.assertTrue('number' not in item.get_deferred_fields())
self.assertEqual(item.tracker.previous('number'), 1)
self.assertFalse(item.tracker.has_changed('number'))

Expand Down
8 changes: 1 addition & 7 deletions tests/test_managers/test_inheritance_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,15 +171,12 @@ def test_filter_on_values_queryset(self):
queryset = InheritanceManagerTestChild1.objects.values('id').filter(pk=self.child1.pk)
self.assertEqual(list(queryset), [{'id': self.child1.pk}])

@skipUnless(django.VERSION >= (1, 9, 0), "test only applies to Django 1.9+")
def test_dj19_values_list_on_select_subclasses(self):
def test_values_list_on_select_subclasses(self):
"""
Using `select_subclasses` in conjunction with `values_list()` raised an
exception in `_get_sub_obj_recurse()` because the result of `values_list()`
is either a `tuple` or primitive objects if `flat=True` is specified,
because no type checking was done prior to fetching child nodes.

Django versions below 1.9 are not affected by this bug.
"""

# Querysets are cast to lists to force immediate evaluation.
Expand Down Expand Up @@ -430,7 +427,6 @@ def test_limit_to_specific_subclass(self):

self.assertEqual([child3], list(results))

@skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+")
def test_limit_to_specific_grandchild_class(self):
grandchild1 = InheritanceManagerTestGrandChild1.objects.get()
results = InheritanceManagerTestParent.objects.instance_of(InheritanceManagerTestGrandChild1)
Expand All @@ -445,7 +441,6 @@ def test_limit_to_child_fetches_grandchildren_as_child_class(self):

self.assertEqual(set(children), set(results))

@skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+")
def test_can_fetch_limited_class_grandchildren(self):
# Not sure if this is the desired behaviour...?
children = InheritanceManagerTestChild1.objects.select_subclasses()
Expand All @@ -462,7 +457,6 @@ def test_selecting_multiple_instance_classes(self):

self.assertEqual(set([child3] + list(children1)), set(results))

@skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+")
def test_selecting_multiple_instance_classes_including_grandchildren(self):
child3 = InheritanceManagerTestChild3.objects.create()
grandchild1 = InheritanceManagerTestGrandChild1.objects.get()
Expand Down
25 changes: 8 additions & 17 deletions tests/test_models/test_deferred_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,9 @@ def test_custom_descriptor_works(self):
def test_deferred(self):
instance = ModelWithCustomDescriptor.objects.only('id').get(
pk=self.instance.pk)
if django.VERSION >= (1, 10):
self.assertIn('custom_field', instance.get_deferred_fields())
else:
self.assertIn('custom_field', instance._deferred_fields)
self.assertIn('custom_field', instance.get_deferred_fields())
self.assertEqual(instance.custom_field, '1')
if django.VERSION >= (1, 10):
self.assertNotIn('custom_field', instance.get_deferred_fields())
else:
self.assertNotIn('custom_field', instance._deferred_fields)
self.assertNotIn('custom_field', instance.get_deferred_fields())
self.assertEqual(instance.regular_field, 1)
self.assertEqual(instance.tracked_custom_field, '1')
self.assertEqual(instance.tracked_regular_field, 1)
Expand All @@ -60,12 +54,9 @@ def test_deferred(self):
self.assertEqual(instance.tracked_regular_field, 2)

instance = ModelWithCustomDescriptor.objects.only('id').get(pk=instance.pk)
if django.VERSION >= (1, 10):
# This fails on 1.8 and 1.9, which is a bug in the deferred field
# implementation on those versions.
instance.tracked_custom_field = 3
self.assertEqual(instance.tracked_custom_field, '3')
self.assertTrue(instance.tracker.has_changed('tracked_custom_field'))
del instance.tracked_custom_field
self.assertEqual(instance.tracked_custom_field, '2')
self.assertFalse(instance.tracker.has_changed('tracked_custom_field'))
instance.tracked_custom_field = 3
self.assertEqual(instance.tracked_custom_field, '3')
self.assertTrue(instance.tracker.has_changed('tracked_custom_field'))
del instance.tracked_custom_field
self.assertEqual(instance.tracked_custom_field, '2')
self.assertFalse(instance.tracker.has_changed('tracked_custom_field'))
4 changes: 0 additions & 4 deletions translations.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@ def run(command):
if not settings.configured:
settings.configure(**DEFAULT_SETTINGS)

# Compatibility with Django 1.7's stricter initialization
if hasattr(django, 'setup'):
django.setup()

parent = os.path.dirname(os.path.abspath(__file__))
appdir = os.path.join(parent, 'model_utils')
os.chdir(appdir)
Expand Down