mirror of
https://github.com/Hopiu/django-model-utils.git
synced 2026-03-17 04:10:24 +00:00
References #83. Instead of patching the save method of a tracked model class, we can use a signal handler on post_save, which means we can still pickle our model class. Note we can't just listen for the signal from the class we have, but instead listen for all post_save signals. This means we actually install a new signal handler for each tracked model class, which fires on all model save occurrences (and returns immediately if this handler doesn't care). We probably could improve this to have a registry of tracked models, or something, that allows us to just install one signal handler, and filter according to membership.
1917 lines
68 KiB
Python
1917 lines
68 KiB
Python
from __future__ import unicode_literals
|
|
|
|
from datetime import datetime, timedelta
|
|
import pickle
|
|
try:
|
|
from unittest import skipUnless
|
|
except ImportError: # Python 2.6
|
|
from django.utils.unittest import skipUnless
|
|
|
|
import django
|
|
from django.db import models
|
|
from django.db.models.fields import FieldDoesNotExist
|
|
from django.utils.six import text_type
|
|
from django.core.exceptions import ImproperlyConfigured, FieldError
|
|
from django.core.management import call_command
|
|
from django.test import TestCase
|
|
|
|
from model_utils import Choices, FieldTracker
|
|
from model_utils.fields import get_excerpt, MonitorField, StatusField
|
|
from model_utils.managers import QueryManager
|
|
from model_utils.models import StatusModel, TimeFramedModel
|
|
from model_utils.tests.models import (
|
|
InheritanceManagerTestRelated, InheritanceManagerTestGrandChild1,
|
|
InheritanceManagerTestGrandChild1_2,
|
|
InheritanceManagerTestParent, InheritanceManagerTestChild1,
|
|
InheritanceManagerTestChild2, TimeStamp, Post, Article, Status,
|
|
StatusPlainTuple, TimeFrame, Monitored, MonitorWhen, MonitorWhenEmpty, StatusManagerAdded,
|
|
TimeFrameManagerAdded, Dude, SplitFieldAbstractParent, Car, Spot,
|
|
ModelTracked, ModelTrackedFK, ModelTrackedNotDefault, ModelTrackedMultiple, InheritedModelTracked,
|
|
Tracked, TrackedFK, TrackedNotDefault, TrackedNonFieldAttr, TrackedMultiple,
|
|
InheritedTracked, StatusFieldDefaultFilled, StatusFieldDefaultNotFilled,
|
|
InheritanceManagerTestChild3, StatusFieldChoicesName)
|
|
|
|
|
|
class MigrationsTests(TestCase):
|
|
@skipUnless(django.VERSION >= (1, 7, 0), "test only applies to Django 1.7+")
|
|
def test_makemigrations(self):
|
|
call_command('makemigrations', dry_run=True)
|
|
|
|
|
|
class GetExcerptTests(TestCase):
|
|
def test_split(self):
|
|
e = get_excerpt("some content\n\n<!-- split -->\n\nsome more")
|
|
self.assertEqual(e, 'some content\n')
|
|
|
|
|
|
def test_auto_split(self):
|
|
e = get_excerpt("para one\n\npara two\n\npara three")
|
|
self.assertEqual(e, 'para one\n\npara two')
|
|
|
|
|
|
def test_middle_of_para(self):
|
|
e = get_excerpt("some text\n<!-- split -->\nmore text")
|
|
self.assertEqual(e, 'some text')
|
|
|
|
|
|
def test_middle_of_line(self):
|
|
e = get_excerpt("some text <!-- split --> more text")
|
|
self.assertEqual(e, "some text <!-- split --> more text")
|
|
|
|
|
|
|
|
class SplitFieldTests(TestCase):
|
|
full_text = 'summary\n\n<!-- split -->\n\nmore'
|
|
excerpt = 'summary\n'
|
|
|
|
|
|
def setUp(self):
|
|
self.post = Article.objects.create(
|
|
title='example post', body=self.full_text)
|
|
|
|
|
|
def test_unicode_content(self):
|
|
self.assertEqual(text_type(self.post.body), self.full_text)
|
|
|
|
|
|
def test_excerpt(self):
|
|
self.assertEqual(self.post.body.excerpt, self.excerpt)
|
|
|
|
|
|
def test_content(self):
|
|
self.assertEqual(self.post.body.content, self.full_text)
|
|
|
|
|
|
def test_has_more(self):
|
|
self.assertTrue(self.post.body.has_more)
|
|
|
|
|
|
def test_not_has_more(self):
|
|
post = Article.objects.create(title='example 2',
|
|
body='some text\n\nsome more\n')
|
|
self.assertFalse(post.body.has_more)
|
|
|
|
|
|
def test_load_back(self):
|
|
post = Article.objects.get(pk=self.post.pk)
|
|
self.assertEqual(post.body.content, self.post.body.content)
|
|
self.assertEqual(post.body.excerpt, self.post.body.excerpt)
|
|
|
|
|
|
def test_assign_to_body(self):
|
|
new_text = 'different\n\n<!-- split -->\n\nother'
|
|
self.post.body = new_text
|
|
self.post.save()
|
|
self.assertEqual(text_type(self.post.body), new_text)
|
|
|
|
|
|
def test_assign_to_content(self):
|
|
new_text = 'different\n\n<!-- split -->\n\nother'
|
|
self.post.body.content = new_text
|
|
self.post.save()
|
|
self.assertEqual(text_type(self.post.body), new_text)
|
|
|
|
|
|
def test_assign_to_excerpt(self):
|
|
with self.assertRaises(AttributeError):
|
|
self.post.body.excerpt = 'this should fail'
|
|
|
|
|
|
def test_access_via_class(self):
|
|
with self.assertRaises(AttributeError):
|
|
Article.body
|
|
|
|
|
|
def test_none(self):
|
|
a = Article(title='Some Title', body=None)
|
|
self.assertEqual(a.body, None)
|
|
|
|
|
|
def test_assign_splittext(self):
|
|
a = Article(title='Some Title')
|
|
a.body = self.post.body
|
|
self.assertEqual(a.body.excerpt, 'summary\n')
|
|
|
|
|
|
def test_value_to_string(self):
|
|
f = self.post._meta.get_field('body')
|
|
self.assertEqual(f.value_to_string(self.post), self.full_text)
|
|
|
|
|
|
def test_abstract_inheritance(self):
|
|
class Child(SplitFieldAbstractParent):
|
|
pass
|
|
|
|
self.assertEqual(
|
|
[f.name for f in Child._meta.fields],
|
|
["id", "content", "_content_excerpt"])
|
|
|
|
|
|
|
|
class MonitorFieldTests(TestCase):
|
|
def setUp(self):
|
|
self.instance = Monitored(name='Charlie')
|
|
self.created = self.instance.name_changed
|
|
|
|
|
|
def test_save_no_change(self):
|
|
self.instance.save()
|
|
self.assertEqual(self.instance.name_changed, self.created)
|
|
|
|
|
|
def test_save_changed(self):
|
|
self.instance.name = 'Maria'
|
|
self.instance.save()
|
|
self.assertTrue(self.instance.name_changed > self.created)
|
|
|
|
|
|
def test_double_save(self):
|
|
self.instance.name = 'Jose'
|
|
self.instance.save()
|
|
changed = self.instance.name_changed
|
|
self.instance.save()
|
|
self.assertEqual(self.instance.name_changed, changed)
|
|
|
|
|
|
def test_no_monitor_arg(self):
|
|
with self.assertRaises(TypeError):
|
|
MonitorField()
|
|
|
|
|
|
|
|
class MonitorWhenFieldTests(TestCase):
|
|
"""
|
|
Will record changes only when name is 'Jose' or 'Maria'
|
|
"""
|
|
def setUp(self):
|
|
self.instance = MonitorWhen(name='Charlie')
|
|
self.created = self.instance.name_changed
|
|
|
|
|
|
def test_save_no_change(self):
|
|
self.instance.save()
|
|
self.assertEqual(self.instance.name_changed, self.created)
|
|
|
|
|
|
def test_save_changed_to_Jose(self):
|
|
self.instance.name = 'Jose'
|
|
self.instance.save()
|
|
self.assertTrue(self.instance.name_changed > self.created)
|
|
|
|
|
|
def test_save_changed_to_Maria(self):
|
|
self.instance.name = 'Maria'
|
|
self.instance.save()
|
|
self.assertTrue(self.instance.name_changed > self.created)
|
|
|
|
|
|
def test_save_changed_to_Pedro(self):
|
|
self.instance.name = 'Pedro'
|
|
self.instance.save()
|
|
self.assertEqual(self.instance.name_changed, self.created)
|
|
|
|
|
|
def test_double_save(self):
|
|
self.instance.name = 'Jose'
|
|
self.instance.save()
|
|
changed = self.instance.name_changed
|
|
self.instance.save()
|
|
self.assertEqual(self.instance.name_changed, changed)
|
|
|
|
|
|
|
|
class MonitorWhenEmptyFieldTests(TestCase):
|
|
"""
|
|
Monitor should never be updated id when is an empty list.
|
|
"""
|
|
def setUp(self):
|
|
self.instance = MonitorWhenEmpty(name='Charlie')
|
|
self.created = self.instance.name_changed
|
|
|
|
|
|
def test_save_no_change(self):
|
|
self.instance.save()
|
|
self.assertEqual(self.instance.name_changed, self.created)
|
|
|
|
|
|
def test_save_changed_to_Jose(self):
|
|
self.instance.name = 'Jose'
|
|
self.instance.save()
|
|
self.assertEqual(self.instance.name_changed, self.created)
|
|
|
|
|
|
def test_save_changed_to_Maria(self):
|
|
self.instance.name = 'Maria'
|
|
self.instance.save()
|
|
self.assertEqual(self.instance.name_changed, self.created)
|
|
|
|
|
|
|
|
class StatusFieldTests(TestCase):
|
|
|
|
def test_status_with_default_filled(self):
|
|
instance = StatusFieldDefaultFilled()
|
|
self.assertEqual(instance.status, instance.STATUS.yes)
|
|
|
|
def test_status_with_default_not_filled(self):
|
|
instance = StatusFieldDefaultNotFilled()
|
|
self.assertEqual(instance.status, instance.STATUS.no)
|
|
|
|
def test_no_check_for_status(self):
|
|
field = StatusField(no_check_for_status=True)
|
|
# this model has no STATUS attribute, so checking for it would error
|
|
field.prepare_class(Article)
|
|
|
|
def test_get_status_display(self):
|
|
instance = StatusFieldDefaultFilled()
|
|
self.assertEqual(instance.get_status_display(), "Yes")
|
|
|
|
def test_choices_name(self):
|
|
StatusFieldChoicesName()
|
|
|
|
|
|
class ChoicesTests(TestCase):
|
|
def setUp(self):
|
|
self.STATUS = Choices('DRAFT', 'PUBLISHED')
|
|
|
|
|
|
def test_getattr(self):
|
|
self.assertEqual(self.STATUS.DRAFT, 'DRAFT')
|
|
|
|
|
|
def test_indexing(self):
|
|
self.assertEqual(self.STATUS['PUBLISHED'], 'PUBLISHED')
|
|
|
|
|
|
def test_iteration(self):
|
|
self.assertEqual(tuple(self.STATUS), (('DRAFT', 'DRAFT'), ('PUBLISHED', 'PUBLISHED')))
|
|
|
|
|
|
def test_len(self):
|
|
self.assertEqual(len(self.STATUS), 2)
|
|
|
|
|
|
def test_repr(self):
|
|
self.assertEqual(repr(self.STATUS), "Choices" + repr((
|
|
('DRAFT', 'DRAFT', 'DRAFT'),
|
|
('PUBLISHED', 'PUBLISHED', 'PUBLISHED'),
|
|
)))
|
|
|
|
|
|
def test_wrong_length_tuple(self):
|
|
with self.assertRaises(ValueError):
|
|
Choices(('a',))
|
|
|
|
|
|
def test_contains_value(self):
|
|
self.assertTrue('PUBLISHED' in self.STATUS)
|
|
self.assertTrue('DRAFT' in self.STATUS)
|
|
|
|
|
|
def test_doesnt_contain_value(self):
|
|
self.assertFalse('UNPUBLISHED' in self.STATUS)
|
|
|
|
def test_deepcopy(self):
|
|
import copy
|
|
self.assertEqual(list(self.STATUS),
|
|
list(copy.deepcopy(self.STATUS)))
|
|
|
|
|
|
def test_equality(self):
|
|
self.assertEqual(self.STATUS, Choices('DRAFT', 'PUBLISHED'))
|
|
|
|
|
|
def test_inequality(self):
|
|
self.assertNotEqual(self.STATUS, ['DRAFT', 'PUBLISHED'])
|
|
self.assertNotEqual(self.STATUS, Choices('DRAFT'))
|
|
|
|
|
|
def test_composability(self):
|
|
self.assertEqual(Choices('DRAFT') + Choices('PUBLISHED'), self.STATUS)
|
|
self.assertEqual(Choices('DRAFT') + ('PUBLISHED',), self.STATUS)
|
|
self.assertEqual(('DRAFT',) + Choices('PUBLISHED'), self.STATUS)
|
|
|
|
|
|
def test_option_groups(self):
|
|
c = Choices(('group a', ['one', 'two']), ['group b', ('three',)])
|
|
self.assertEqual(
|
|
list(c),
|
|
[
|
|
('group a', [('one', 'one'), ('two', 'two')]),
|
|
('group b', [('three', 'three')]),
|
|
],
|
|
)
|
|
|
|
|
|
class LabelChoicesTests(ChoicesTests):
|
|
def setUp(self):
|
|
self.STATUS = Choices(
|
|
('DRAFT', 'is draft'),
|
|
('PUBLISHED', 'is published'),
|
|
'DELETED',
|
|
)
|
|
|
|
|
|
def test_iteration(self):
|
|
self.assertEqual(tuple(self.STATUS), (
|
|
('DRAFT', 'is draft'),
|
|
('PUBLISHED', 'is published'),
|
|
('DELETED', 'DELETED'))
|
|
)
|
|
|
|
|
|
def test_indexing(self):
|
|
self.assertEqual(self.STATUS['PUBLISHED'], 'is published')
|
|
|
|
|
|
def test_default(self):
|
|
self.assertEqual(self.STATUS.DELETED, 'DELETED')
|
|
|
|
|
|
def test_provided(self):
|
|
self.assertEqual(self.STATUS.DRAFT, 'DRAFT')
|
|
|
|
|
|
def test_len(self):
|
|
self.assertEqual(len(self.STATUS), 3)
|
|
|
|
|
|
def test_equality(self):
|
|
self.assertEqual(self.STATUS, Choices(
|
|
('DRAFT', 'is draft'),
|
|
('PUBLISHED', 'is published'),
|
|
'DELETED',
|
|
))
|
|
|
|
|
|
def test_inequality(self):
|
|
self.assertNotEqual(self.STATUS, [
|
|
('DRAFT', 'is draft'),
|
|
('PUBLISHED', 'is published'),
|
|
'DELETED'
|
|
])
|
|
self.assertNotEqual(self.STATUS, Choices('DRAFT'))
|
|
|
|
|
|
def test_repr(self):
|
|
self.assertEqual(repr(self.STATUS), "Choices" + repr((
|
|
('DRAFT', 'DRAFT', 'is draft'),
|
|
('PUBLISHED', 'PUBLISHED', 'is published'),
|
|
('DELETED', 'DELETED', 'DELETED'),
|
|
)))
|
|
|
|
|
|
def test_contains_value(self):
|
|
self.assertTrue('PUBLISHED' in self.STATUS)
|
|
self.assertTrue('DRAFT' in self.STATUS)
|
|
# This should be True, because both the display value
|
|
# and the internal representation are both DELETED.
|
|
self.assertTrue('DELETED' in self.STATUS)
|
|
|
|
|
|
def test_doesnt_contain_value(self):
|
|
self.assertFalse('UNPUBLISHED' in self.STATUS)
|
|
|
|
|
|
def test_doesnt_contain_display_value(self):
|
|
self.assertFalse('is draft' in self.STATUS)
|
|
|
|
|
|
def test_composability(self):
|
|
self.assertEqual(
|
|
Choices(('DRAFT', 'is draft',)) + Choices(('PUBLISHED', 'is published'), 'DELETED'),
|
|
self.STATUS
|
|
)
|
|
|
|
self.assertEqual(
|
|
(('DRAFT', 'is draft',),) + Choices(('PUBLISHED', 'is published'), 'DELETED'),
|
|
self.STATUS
|
|
)
|
|
|
|
self.assertEqual(
|
|
Choices(('DRAFT', 'is draft',)) + (('PUBLISHED', 'is published'), 'DELETED'),
|
|
self.STATUS
|
|
)
|
|
|
|
|
|
def test_option_groups(self):
|
|
c = Choices(
|
|
('group a', [(1, 'one'), (2, 'two')]),
|
|
['group b', ((3, 'three'),)]
|
|
)
|
|
self.assertEqual(
|
|
list(c),
|
|
[
|
|
('group a', [(1, 'one'), (2, 'two')]),
|
|
('group b', [(3, 'three')]),
|
|
],
|
|
)
|
|
|
|
|
|
|
|
class IdentifierChoicesTests(ChoicesTests):
|
|
def setUp(self):
|
|
self.STATUS = Choices(
|
|
(0, 'DRAFT', 'is draft'),
|
|
(1, 'PUBLISHED', 'is published'),
|
|
(2, 'DELETED', 'is deleted'))
|
|
|
|
|
|
def test_iteration(self):
|
|
self.assertEqual(tuple(self.STATUS), (
|
|
(0, 'is draft'),
|
|
(1, 'is published'),
|
|
(2, 'is deleted')))
|
|
|
|
|
|
def test_indexing(self):
|
|
self.assertEqual(self.STATUS[1], 'is published')
|
|
|
|
|
|
def test_getattr(self):
|
|
self.assertEqual(self.STATUS.DRAFT, 0)
|
|
|
|
|
|
def test_len(self):
|
|
self.assertEqual(len(self.STATUS), 3)
|
|
|
|
|
|
def test_repr(self):
|
|
self.assertEqual(repr(self.STATUS), "Choices" + repr((
|
|
(0, 'DRAFT', 'is draft'),
|
|
(1, 'PUBLISHED', 'is published'),
|
|
(2, 'DELETED', 'is deleted'),
|
|
)))
|
|
|
|
|
|
def test_contains_value(self):
|
|
self.assertTrue(0 in self.STATUS)
|
|
self.assertTrue(1 in self.STATUS)
|
|
self.assertTrue(2 in self.STATUS)
|
|
|
|
|
|
def test_doesnt_contain_value(self):
|
|
self.assertFalse(3 in self.STATUS)
|
|
|
|
|
|
def test_doesnt_contain_display_value(self):
|
|
self.assertFalse('is draft' in self.STATUS)
|
|
|
|
|
|
def test_doesnt_contain_python_attr(self):
|
|
self.assertFalse('PUBLISHED' in self.STATUS)
|
|
|
|
|
|
def test_equality(self):
|
|
self.assertEqual(self.STATUS, Choices(
|
|
(0, 'DRAFT', 'is draft'),
|
|
(1, 'PUBLISHED', 'is published'),
|
|
(2, 'DELETED', 'is deleted')
|
|
))
|
|
|
|
|
|
def test_inequality(self):
|
|
self.assertNotEqual(self.STATUS, [
|
|
(0, 'DRAFT', 'is draft'),
|
|
(1, 'PUBLISHED', 'is published'),
|
|
(2, 'DELETED', 'is deleted')
|
|
])
|
|
self.assertNotEqual(self.STATUS, Choices('DRAFT'))
|
|
|
|
|
|
def test_composability(self):
|
|
self.assertEqual(
|
|
Choices(
|
|
(0, 'DRAFT', 'is draft'),
|
|
(1, 'PUBLISHED', 'is published')
|
|
) + Choices(
|
|
(2, 'DELETED', 'is deleted'),
|
|
),
|
|
self.STATUS
|
|
)
|
|
|
|
self.assertEqual(
|
|
Choices(
|
|
(0, 'DRAFT', 'is draft'),
|
|
(1, 'PUBLISHED', 'is published')
|
|
) + (
|
|
(2, 'DELETED', 'is deleted'),
|
|
),
|
|
self.STATUS
|
|
)
|
|
|
|
self.assertEqual(
|
|
(
|
|
(0, 'DRAFT', 'is draft'),
|
|
(1, 'PUBLISHED', 'is published')
|
|
) + Choices(
|
|
(2, 'DELETED', 'is deleted'),
|
|
),
|
|
self.STATUS
|
|
)
|
|
|
|
|
|
def test_option_groups(self):
|
|
c = Choices(
|
|
('group a', [(1, 'ONE', 'one'), (2, 'TWO', 'two')]),
|
|
['group b', ((3, 'THREE', 'three'),)]
|
|
)
|
|
self.assertEqual(
|
|
list(c),
|
|
[
|
|
('group a', [(1, 'one'), (2, 'two')]),
|
|
('group b', [(3, 'three')]),
|
|
],
|
|
)
|
|
|
|
|
|
class InheritanceManagerTests(TestCase):
|
|
def setUp(self):
|
|
self.child1 = InheritanceManagerTestChild1.objects.create()
|
|
self.child2 = InheritanceManagerTestChild2.objects.create()
|
|
self.grandchild1 = InheritanceManagerTestGrandChild1.objects.create()
|
|
self.grandchild1_2 = \
|
|
InheritanceManagerTestGrandChild1_2.objects.create()
|
|
|
|
|
|
def get_manager(self):
|
|
return InheritanceManagerTestParent.objects
|
|
|
|
|
|
def test_normal(self):
|
|
children = set([
|
|
InheritanceManagerTestParent(pk=self.child1.pk),
|
|
InheritanceManagerTestParent(pk=self.child2.pk),
|
|
InheritanceManagerTestParent(pk=self.grandchild1.pk),
|
|
InheritanceManagerTestParent(pk=self.grandchild1_2.pk),
|
|
])
|
|
self.assertEqual(set(self.get_manager().all()), children)
|
|
|
|
|
|
def test_select_all_subclasses(self):
|
|
children = set([self.child1, self.child2])
|
|
if django.VERSION >= (1, 6, 0):
|
|
children.add(self.grandchild1)
|
|
children.add(self.grandchild1_2)
|
|
else:
|
|
children.add(InheritanceManagerTestChild1(pk=self.grandchild1.pk))
|
|
children.add(InheritanceManagerTestChild1(pk=self.grandchild1_2.pk))
|
|
self.assertEqual(
|
|
set(self.get_manager().select_subclasses()), children)
|
|
|
|
|
|
def test_select_subclasses_invalid_relation(self):
|
|
"""
|
|
If an invalid relation string is provided, we can provide the user
|
|
with a list which is valid, rather than just have the select_related()
|
|
raise an AttributeError further in.
|
|
"""
|
|
regex = '^.+? is not in the discovered subclasses, tried:.+$'
|
|
with self.assertRaisesRegexp(ValueError, regex):
|
|
self.get_manager().select_subclasses('user')
|
|
|
|
|
|
def test_select_specific_subclasses(self):
|
|
children = set([
|
|
self.child1,
|
|
InheritanceManagerTestParent(pk=self.child2.pk),
|
|
InheritanceManagerTestChild1(pk=self.grandchild1.pk),
|
|
InheritanceManagerTestChild1(pk=self.grandchild1_2.pk),
|
|
])
|
|
self.assertEqual(
|
|
set(
|
|
self.get_manager().select_subclasses(
|
|
"inheritancemanagertestchild1")
|
|
),
|
|
children,
|
|
)
|
|
|
|
|
|
@skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+")
|
|
def test_select_specific_grandchildren(self):
|
|
children = set([
|
|
InheritanceManagerTestParent(pk=self.child1.pk),
|
|
InheritanceManagerTestParent(pk=self.child2.pk),
|
|
self.grandchild1,
|
|
InheritanceManagerTestParent(pk=self.grandchild1_2.pk),
|
|
])
|
|
self.assertEqual(
|
|
set(
|
|
self.get_manager().select_subclasses(
|
|
"inheritancemanagertestchild1__inheritancemanagertestgrandchild1"
|
|
)
|
|
),
|
|
children,
|
|
)
|
|
|
|
|
|
@skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+")
|
|
def test_children_and_grandchildren(self):
|
|
children = set([
|
|
self.child1,
|
|
InheritanceManagerTestParent(pk=self.child2.pk),
|
|
self.grandchild1,
|
|
InheritanceManagerTestChild1(pk=self.grandchild1_2.pk),
|
|
])
|
|
self.assertEqual(
|
|
set(
|
|
self.get_manager().select_subclasses(
|
|
"inheritancemanagertestchild1",
|
|
"inheritancemanagertestchild1__inheritancemanagertestgrandchild1"
|
|
)
|
|
),
|
|
children,
|
|
)
|
|
|
|
|
|
def test_get_subclass(self):
|
|
self.assertEqual(
|
|
self.get_manager().get_subclass(pk=self.child1.pk),
|
|
self.child1)
|
|
|
|
|
|
def test_get_subclass_on_queryset(self):
|
|
self.assertEqual(
|
|
self.get_manager().all().get_subclass(pk=self.child1.pk),
|
|
self.child1)
|
|
|
|
|
|
def test_prior_select_related(self):
|
|
with self.assertNumQueries(1):
|
|
obj = self.get_manager().select_related(
|
|
"inheritancemanagertestchild1").select_subclasses(
|
|
"inheritancemanagertestchild2").get(pk=self.child1.pk)
|
|
obj.inheritancemanagertestchild1
|
|
|
|
|
|
@skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+")
|
|
def test_version_determining_any_depth(self):
|
|
self.assertIsNone(self.get_manager().all()._get_maximum_depth())
|
|
|
|
|
|
@skipUnless(django.VERSION < (1, 6, 0), "test only applies to Django < 1.6")
|
|
def test_version_determining_only_child_depth(self):
|
|
self.assertEqual(1, self.get_manager().all()._get_maximum_depth())
|
|
|
|
|
|
@skipUnless(django.VERSION < (1, 6, 0), "test only applies to Django < 1.6")
|
|
def test_manually_specifying_parent_fk_only_children(self):
|
|
"""
|
|
given a Model which inherits from another Model, but also declares
|
|
the OneToOne link manually using `related_name` and `parent_link`,
|
|
ensure that the relation names and subclasses are obtained correctly.
|
|
"""
|
|
child3 = InheritanceManagerTestChild3.objects.create()
|
|
results = InheritanceManagerTestParent.objects.all().select_subclasses()
|
|
|
|
expected_objs = [self.child1, self.child2,
|
|
InheritanceManagerTestChild1(pk=self.grandchild1.pk),
|
|
InheritanceManagerTestChild1(pk=self.grandchild1_2.pk),
|
|
child3]
|
|
self.assertEqual(list(results), expected_objs)
|
|
|
|
expected_related_names = [
|
|
'inheritancemanagertestchild1',
|
|
'inheritancemanagertestchild2',
|
|
'manual_onetoone', # this was set via parent_link & related_name
|
|
]
|
|
self.assertEqual(set(results.subclasses),
|
|
set(expected_related_names))
|
|
|
|
|
|
@skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+")
|
|
def test_manually_specifying_parent_fk_including_grandchildren(self):
|
|
"""
|
|
given a Model which inherits from another Model, but also declares
|
|
the OneToOne link manually using `related_name` and `parent_link`,
|
|
ensure that the relation names and subclasses are obtained correctly.
|
|
"""
|
|
child3 = InheritanceManagerTestChild3.objects.create()
|
|
results = InheritanceManagerTestParent.objects.all().select_subclasses()
|
|
|
|
expected_objs = [self.child1, self.child2, self.grandchild1,
|
|
self.grandchild1_2, child3]
|
|
self.assertEqual(list(results), expected_objs)
|
|
|
|
expected_related_names = [
|
|
'inheritancemanagertestchild1__inheritancemanagertestgrandchild1',
|
|
'inheritancemanagertestchild1__inheritancemanagertestgrandchild1_2',
|
|
'inheritancemanagertestchild1',
|
|
'inheritancemanagertestchild2',
|
|
'manual_onetoone', # this was set via parent_link & related_name
|
|
]
|
|
self.assertEqual(set(results.subclasses),
|
|
set(expected_related_names))
|
|
|
|
|
|
def test_manually_specifying_parent_fk_single_subclass(self):
|
|
"""
|
|
Using a string related_name when the relation is manually defined
|
|
instead of implicit should still work in the same way.
|
|
"""
|
|
related_name = 'manual_onetoone'
|
|
child3 = InheritanceManagerTestChild3.objects.create()
|
|
results = InheritanceManagerTestParent.objects.all().select_subclasses(related_name)
|
|
|
|
expected_objs = [InheritanceManagerTestParent(pk=self.child1.pk),
|
|
InheritanceManagerTestParent(pk=self.child2.pk),
|
|
InheritanceManagerTestParent(pk=self.grandchild1.pk),
|
|
InheritanceManagerTestParent(pk=self.grandchild1_2.pk),
|
|
child3]
|
|
self.assertEqual(list(results), expected_objs)
|
|
expected_related_names = [related_name]
|
|
self.assertEqual(set(results.subclasses),
|
|
set(expected_related_names))
|
|
|
|
|
|
class InheritanceManagerUsingModelsTests(TestCase):
|
|
|
|
def setUp(self):
|
|
self.parent1 = InheritanceManagerTestParent.objects.create()
|
|
self.child1 = InheritanceManagerTestChild1.objects.create()
|
|
self.child2 = InheritanceManagerTestChild2.objects.create()
|
|
self.grandchild1 = InheritanceManagerTestGrandChild1.objects.create()
|
|
self.grandchild1_2 = InheritanceManagerTestGrandChild1_2.objects.create()
|
|
|
|
|
|
def test_select_subclass_by_child_model(self):
|
|
"""
|
|
Confirm that passing a child model works the same as passing the
|
|
select_related manually
|
|
"""
|
|
objs = InheritanceManagerTestParent.objects.select_subclasses(
|
|
"inheritancemanagertestchild1").order_by('pk')
|
|
objsmodels = InheritanceManagerTestParent.objects.select_subclasses(
|
|
InheritanceManagerTestChild1).order_by('pk')
|
|
self.assertEqual(objs.subclasses, objsmodels.subclasses)
|
|
self.assertEqual(list(objs), list(objsmodels))
|
|
|
|
|
|
@skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+")
|
|
def test_select_subclass_by_grandchild_model(self):
|
|
"""
|
|
Confirm that passing a grandchild model works the same as passing the
|
|
select_related manually
|
|
"""
|
|
objs = InheritanceManagerTestParent.objects.select_subclasses(
|
|
"inheritancemanagertestchild1__inheritancemanagertestgrandchild1")\
|
|
.order_by('pk')
|
|
objsmodels = InheritanceManagerTestParent.objects.select_subclasses(
|
|
InheritanceManagerTestGrandChild1).order_by('pk')
|
|
self.assertEqual(objs.subclasses, objsmodels.subclasses)
|
|
self.assertEqual(list(objs), list(objsmodels))
|
|
|
|
|
|
@skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+")
|
|
def test_selecting_all_subclasses_specifically_grandchildren(self):
|
|
"""
|
|
A bare select_subclasses() should achieve the same results as doing
|
|
select_subclasses and specifying all possible subclasses.
|
|
This test checks grandchildren, so only works on 1.6>=
|
|
"""
|
|
objs = InheritanceManagerTestParent.objects.select_subclasses().order_by('pk')
|
|
objsmodels = InheritanceManagerTestParent.objects.select_subclasses(
|
|
InheritanceManagerTestChild1, InheritanceManagerTestChild2,
|
|
InheritanceManagerTestChild3,
|
|
InheritanceManagerTestGrandChild1,
|
|
InheritanceManagerTestGrandChild1_2).order_by('pk')
|
|
self.assertEqual(set(objs.subclasses), set(objsmodels.subclasses))
|
|
self.assertEqual(list(objs), list(objsmodels))
|
|
|
|
|
|
def test_selecting_all_subclasses_specifically_children(self):
|
|
"""
|
|
A bare select_subclasses() should achieve the same results as doing
|
|
select_subclasses and specifying all possible subclasses.
|
|
|
|
Note: This is sort of the same test as
|
|
`test_selecting_all_subclasses_specifically_grandchildren` but it
|
|
specifically switches what models are used because that happens
|
|
behind the scenes in a bare select_subclasses(), so we need to
|
|
emulate it.
|
|
"""
|
|
objs = InheritanceManagerTestParent.objects.select_subclasses().order_by('pk')
|
|
|
|
if django.VERSION >= (1, 6, 0):
|
|
models = (InheritanceManagerTestChild1,
|
|
InheritanceManagerTestChild2,
|
|
InheritanceManagerTestChild3,
|
|
InheritanceManagerTestGrandChild1,
|
|
InheritanceManagerTestGrandChild1_2)
|
|
else:
|
|
models = (InheritanceManagerTestChild1,
|
|
InheritanceManagerTestChild2,
|
|
InheritanceManagerTestChild3)
|
|
|
|
objsmodels = InheritanceManagerTestParent.objects.select_subclasses(
|
|
*models).order_by('pk')
|
|
# order shouldn't matter, I don't think, as long as the resulting
|
|
# queryset (when cast to a list) is the same.
|
|
self.assertEqual(set(objs.subclasses), set(objsmodels.subclasses))
|
|
self.assertEqual(list(objs), list(objsmodels))
|
|
|
|
|
|
def test_select_subclass_just_self(self):
|
|
"""
|
|
Passing in the same model as the manager/queryset is bound against
|
|
(ie: the root parent) should have no effect on the result set.
|
|
"""
|
|
objsmodels = InheritanceManagerTestParent.objects.select_subclasses(
|
|
InheritanceManagerTestParent).order_by('pk')
|
|
self.assertEqual([], objsmodels.subclasses)
|
|
self.assertEqual(list(objsmodels), [
|
|
InheritanceManagerTestParent(pk=self.parent1.pk),
|
|
InheritanceManagerTestParent(pk=self.child1.pk),
|
|
InheritanceManagerTestParent(pk=self.child2.pk),
|
|
InheritanceManagerTestParent(pk=self.grandchild1.pk),
|
|
InheritanceManagerTestParent(pk=self.grandchild1_2.pk),
|
|
])
|
|
|
|
|
|
def test_select_subclass_invalid_related_model(self):
|
|
"""
|
|
Confirming that giving a stupid model doesn't work.
|
|
"""
|
|
from django.contrib.auth.models import User
|
|
regex = '^.+? is not a subclass of .+$'
|
|
with self.assertRaisesRegexp(ValueError, regex):
|
|
InheritanceManagerTestParent.objects.select_subclasses(
|
|
User).order_by('pk')
|
|
|
|
|
|
|
|
@skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+")
|
|
def test_mixing_strings_and_classes_with_grandchildren(self):
|
|
"""
|
|
Given arguments consisting of both strings and model classes,
|
|
ensure the right resolutions take place, accounting for the extra
|
|
depth (grandchildren etc) 1.6> allows.
|
|
"""
|
|
objs = InheritanceManagerTestParent.objects.select_subclasses(
|
|
"inheritancemanagertestchild2",
|
|
InheritanceManagerTestGrandChild1_2).order_by('pk')
|
|
expecting = ['inheritancemanagertestchild1__inheritancemanagertestgrandchild1_2',
|
|
'inheritancemanagertestchild2']
|
|
self.assertEqual(set(objs.subclasses), set(expecting))
|
|
expecting2 = [
|
|
InheritanceManagerTestParent(pk=self.parent1.pk),
|
|
InheritanceManagerTestParent(pk=self.child1.pk),
|
|
InheritanceManagerTestChild2(pk=self.child2.pk),
|
|
InheritanceManagerTestParent(pk=self.grandchild1.pk),
|
|
InheritanceManagerTestGrandChild1_2(pk=self.grandchild1_2.pk),
|
|
]
|
|
self.assertEqual(list(objs), expecting2)
|
|
|
|
|
|
def test_mixing_strings_and_classes_with_children(self):
|
|
"""
|
|
Given arguments consisting of both strings and model classes,
|
|
ensure the right resolutions take place, walking down as far as
|
|
children.
|
|
"""
|
|
objs = InheritanceManagerTestParent.objects.select_subclasses(
|
|
"inheritancemanagertestchild2",
|
|
InheritanceManagerTestChild1).order_by('pk')
|
|
expecting = ['inheritancemanagertestchild1',
|
|
'inheritancemanagertestchild2']
|
|
|
|
self.assertEqual(set(objs.subclasses), set(expecting))
|
|
expecting2 = [
|
|
InheritanceManagerTestParent(pk=self.parent1.pk),
|
|
InheritanceManagerTestChild1(pk=self.child1.pk),
|
|
InheritanceManagerTestChild2(pk=self.child2.pk),
|
|
InheritanceManagerTestChild1(pk=self.grandchild1.pk),
|
|
InheritanceManagerTestChild1(pk=self.grandchild1_2.pk),
|
|
]
|
|
self.assertEqual(list(objs), expecting2)
|
|
|
|
|
|
def test_duplications(self):
|
|
"""
|
|
Check that even if the same thing is provided as a string and a model
|
|
that the right results are retrieved.
|
|
"""
|
|
# mixing strings and models which evaluate to the same thing is fine.
|
|
objs = InheritanceManagerTestParent.objects.select_subclasses(
|
|
"inheritancemanagertestchild2",
|
|
InheritanceManagerTestChild2).order_by('pk')
|
|
self.assertEqual(list(objs), [
|
|
InheritanceManagerTestParent(pk=self.parent1.pk),
|
|
InheritanceManagerTestParent(pk=self.child1.pk),
|
|
InheritanceManagerTestChild2(pk=self.child2.pk),
|
|
InheritanceManagerTestParent(pk=self.grandchild1.pk),
|
|
InheritanceManagerTestParent(pk=self.grandchild1_2.pk),
|
|
])
|
|
|
|
|
|
@skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+")
|
|
def test_child_doesnt_accidentally_get_parent(self):
|
|
"""
|
|
Given a Child model which also has an InheritanceManager,
|
|
none of the returned objects should be Parent objects.
|
|
"""
|
|
objs = InheritanceManagerTestChild1.objects.select_subclasses(
|
|
InheritanceManagerTestGrandChild1).order_by('pk')
|
|
self.assertEqual([
|
|
InheritanceManagerTestChild1(pk=self.child1.pk),
|
|
InheritanceManagerTestGrandChild1(pk=self.grandchild1.pk),
|
|
InheritanceManagerTestChild1(pk=self.grandchild1_2.pk),
|
|
], list(objs))
|
|
|
|
|
|
def test_manually_specifying_parent_fk_only_specific_child(self):
|
|
"""
|
|
given a Model which inherits from another Model, but also declares
|
|
the OneToOne link manually using `related_name` and `parent_link`,
|
|
ensure that the relation names and subclasses are obtained correctly.
|
|
"""
|
|
child3 = InheritanceManagerTestChild3.objects.create()
|
|
results = InheritanceManagerTestParent.objects.all().select_subclasses(
|
|
InheritanceManagerTestChild3)
|
|
|
|
expected_objs = [InheritanceManagerTestParent(pk=self.parent1.pk),
|
|
InheritanceManagerTestParent(pk=self.child1.pk),
|
|
InheritanceManagerTestParent(pk=self.child2.pk),
|
|
InheritanceManagerTestParent(pk=self.grandchild1.pk),
|
|
InheritanceManagerTestParent(pk=self.grandchild1_2.pk),
|
|
child3]
|
|
self.assertEqual(list(results), expected_objs)
|
|
|
|
expected_related_names = ['manual_onetoone']
|
|
self.assertEqual(set(results.subclasses),
|
|
set(expected_related_names))
|
|
|
|
def test_extras_descend(self):
|
|
"""
|
|
Ensure that extra(select=) values are copied onto sub-classes.
|
|
"""
|
|
results = InheritanceManagerTestParent.objects.select_subclasses().extra(
|
|
select={'foo': 'id + 1'}
|
|
)
|
|
self.assertTrue(all(result.foo == (result.id + 1) for result in results))
|
|
|
|
|
|
class InheritanceManagerRelatedTests(InheritanceManagerTests):
|
|
def setUp(self):
|
|
self.related = InheritanceManagerTestRelated.objects.create()
|
|
self.child1 = InheritanceManagerTestChild1.objects.create(
|
|
related=self.related)
|
|
self.child2 = InheritanceManagerTestChild2.objects.create(
|
|
related=self.related)
|
|
self.grandchild1 = InheritanceManagerTestGrandChild1.objects.create(related=self.related)
|
|
self.grandchild1_2 = InheritanceManagerTestGrandChild1_2.objects.create(related=self.related)
|
|
|
|
|
|
def get_manager(self):
|
|
return self.related.imtests
|
|
|
|
|
|
def test_get_method_with_select_subclasses(self):
|
|
self.assertEqual(
|
|
InheritanceManagerTestParent.objects.select_subclasses().get(
|
|
id=self.child1.id),
|
|
self.child1)
|
|
|
|
|
|
def test_annotate_with_select_subclasses(self):
|
|
qs = InheritanceManagerTestParent.objects.select_subclasses().annotate(
|
|
models.Count('id'))
|
|
self.assertEqual(qs.get(id=self.child1.id).id__count, 1)
|
|
|
|
|
|
def test_annotate_with_named_arguments_with_select_subclasses(self):
|
|
qs = InheritanceManagerTestParent.objects.select_subclasses().annotate(
|
|
test_count=models.Count('id'))
|
|
self.assertEqual(qs.get(id=self.child1.id).test_count, 1)
|
|
|
|
|
|
def test_annotate_before_select_subclasses(self):
|
|
qs = InheritanceManagerTestParent.objects.annotate(
|
|
models.Count('id')).select_subclasses()
|
|
self.assertEqual(qs.get(id=self.child1.id).id__count, 1)
|
|
|
|
|
|
def test_annotate_with_named_arguments_before_select_subclasses(self):
|
|
qs = InheritanceManagerTestParent.objects.annotate(
|
|
test_count=models.Count('id')).select_subclasses()
|
|
self.assertEqual(qs.get(id=self.child1.id).test_count, 1)
|
|
|
|
|
|
|
|
class TimeStampedModelTests(TestCase):
|
|
def test_created(self):
|
|
t1 = TimeStamp.objects.create()
|
|
t2 = TimeStamp.objects.create()
|
|
self.assertTrue(t2.created > t1.created)
|
|
|
|
|
|
def test_modified(self):
|
|
t1 = TimeStamp.objects.create()
|
|
t2 = TimeStamp.objects.create()
|
|
t1.save()
|
|
self.assertTrue(t2.modified < t1.modified)
|
|
|
|
|
|
|
|
class TimeFramedModelTests(TestCase):
|
|
def setUp(self):
|
|
self.now = datetime.now()
|
|
|
|
|
|
def test_not_yet_begun(self):
|
|
TimeFrame.objects.create(start=self.now+timedelta(days=2))
|
|
self.assertEqual(TimeFrame.timeframed.count(), 0)
|
|
|
|
|
|
def test_finished(self):
|
|
TimeFrame.objects.create(end=self.now-timedelta(days=1))
|
|
self.assertEqual(TimeFrame.timeframed.count(), 0)
|
|
|
|
|
|
def test_no_end(self):
|
|
TimeFrame.objects.create(start=self.now-timedelta(days=10))
|
|
self.assertEqual(TimeFrame.timeframed.count(), 1)
|
|
|
|
|
|
def test_no_start(self):
|
|
TimeFrame.objects.create(end=self.now+timedelta(days=2))
|
|
self.assertEqual(TimeFrame.timeframed.count(), 1)
|
|
|
|
|
|
def test_within_range(self):
|
|
TimeFrame.objects.create(start=self.now-timedelta(days=1),
|
|
end=self.now+timedelta(days=1))
|
|
self.assertEqual(TimeFrame.timeframed.count(), 1)
|
|
|
|
|
|
|
|
class TimeFrameManagerAddedTests(TestCase):
|
|
def test_manager_available(self):
|
|
self.assertTrue(isinstance(TimeFrameManagerAdded.timeframed, QueryManager))
|
|
|
|
|
|
def test_conflict_error(self):
|
|
with self.assertRaises(ImproperlyConfigured):
|
|
class ErrorModel(TimeFramedModel):
|
|
timeframed = models.BooleanField()
|
|
|
|
|
|
|
|
class StatusModelTests(TestCase):
|
|
def setUp(self):
|
|
self.model = Status
|
|
self.on_hold = Status.STATUS.on_hold
|
|
self.active = Status.STATUS.active
|
|
|
|
|
|
def test_created(self):
|
|
c1 = self.model.objects.create()
|
|
c2 = self.model.objects.create()
|
|
self.assertTrue(c2.status_changed > c1.status_changed)
|
|
self.assertEqual(self.model.active.count(), 2)
|
|
self.assertEqual(self.model.deleted.count(), 0)
|
|
|
|
|
|
def test_modification(self):
|
|
t1 = self.model.objects.create()
|
|
date_created = t1.status_changed
|
|
t1.status = self.on_hold
|
|
t1.save()
|
|
self.assertEqual(self.model.active.count(), 0)
|
|
self.assertEqual(self.model.on_hold.count(), 1)
|
|
self.assertTrue(t1.status_changed > date_created)
|
|
date_changed = t1.status_changed
|
|
t1.save()
|
|
self.assertEqual(t1.status_changed, date_changed)
|
|
date_active_again = t1.status_changed
|
|
t1.status = self.active
|
|
t1.save()
|
|
self.assertTrue(t1.status_changed > date_active_again)
|
|
|
|
|
|
|
|
class StatusModelPlainTupleTests(StatusModelTests):
|
|
def setUp(self):
|
|
self.model = StatusPlainTuple
|
|
self.on_hold = StatusPlainTuple.STATUS[2][0]
|
|
self.active = StatusPlainTuple.STATUS[0][0]
|
|
|
|
|
|
|
|
class StatusManagerAddedTests(TestCase):
|
|
def test_manager_available(self):
|
|
self.assertTrue(isinstance(StatusManagerAdded.active, QueryManager))
|
|
|
|
|
|
def test_conflict_error(self):
|
|
with self.assertRaises(ImproperlyConfigured):
|
|
class ErrorModel(StatusModel):
|
|
STATUS = (
|
|
('active', 'Is Active'),
|
|
('deleted', 'Is Deleted'),
|
|
)
|
|
active = models.BooleanField()
|
|
|
|
|
|
|
|
class QueryManagerTests(TestCase):
|
|
def setUp(self):
|
|
data = ((True, True, 0),
|
|
(True, False, 4),
|
|
(False, False, 2),
|
|
(False, True, 3),
|
|
(True, True, 1),
|
|
(True, False, 5))
|
|
for p, c, o in data:
|
|
Post.objects.create(published=p, confirmed=c, order=o)
|
|
|
|
|
|
def test_passing_kwargs(self):
|
|
qs = Post.public.all()
|
|
self.assertEqual([p.order for p in qs], [0, 1, 4, 5])
|
|
|
|
|
|
def test_passing_Q(self):
|
|
qs = Post.public_confirmed.all()
|
|
self.assertEqual([p.order for p in qs], [0, 1])
|
|
|
|
|
|
def test_ordering(self):
|
|
qs = Post.public_reversed.all()
|
|
self.assertEqual([p.order for p in qs], [5, 4, 1, 0])
|
|
|
|
|
|
|
|
try:
|
|
from south.modelsinspector import introspector
|
|
except ImportError:
|
|
introspector = None
|
|
|
|
@skipUnless(introspector, 'South is not installed')
|
|
class SouthFreezingTests(TestCase):
|
|
def test_introspector_adds_no_excerpt_field(self):
|
|
mf = Article._meta.get_field('body')
|
|
args, kwargs = introspector(mf)
|
|
self.assertEqual(kwargs['no_excerpt_field'], 'True')
|
|
|
|
|
|
def test_no_excerpt_field_works(self):
|
|
from .models import NoRendered
|
|
with self.assertRaises(FieldDoesNotExist):
|
|
NoRendered._meta.get_field('_body_excerpt')
|
|
|
|
def test_status_field_no_check_for_status(self):
|
|
sf = StatusFieldDefaultFilled._meta.get_field('status')
|
|
args, kwargs = introspector(sf)
|
|
self.assertEqual(kwargs['no_check_for_status'], 'True')
|
|
|
|
|
|
|
|
class PassThroughManagerTests(TestCase):
|
|
def setUp(self):
|
|
Dude.objects.create(name='The Dude', abides=True, has_rug=False)
|
|
Dude.objects.create(name='His Dudeness', abides=False, has_rug=True)
|
|
Dude.objects.create(name='Duder', abides=False, has_rug=False)
|
|
Dude.objects.create(name='El Duderino', abides=True, has_rug=True)
|
|
|
|
|
|
def test_chaining(self):
|
|
self.assertEqual(Dude.objects.by_name('Duder').count(), 1)
|
|
self.assertEqual(Dude.objects.all().by_name('Duder').count(), 1)
|
|
self.assertEqual(Dude.abiders.rug_positive().count(), 1)
|
|
self.assertEqual(Dude.abiders.all().rug_positive().count(), 1)
|
|
|
|
|
|
def test_manager_only_methods(self):
|
|
stats = Dude.abiders.get_stats()
|
|
self.assertEqual(stats['rug_count'], 1)
|
|
with self.assertRaises(AttributeError):
|
|
Dude.abiders.all().get_stats()
|
|
|
|
|
|
def test_queryset_pickling(self):
|
|
qs = Dude.objects.all()
|
|
saltyqs = pickle.dumps(qs)
|
|
unqs = pickle.loads(saltyqs)
|
|
self.assertEqual(unqs.by_name('The Dude').count(), 1)
|
|
|
|
|
|
def test_queryset_not_available_on_related_manager(self):
|
|
dude = Dude.objects.by_name('Duder').get()
|
|
Car.objects.create(name='Ford', owner=dude)
|
|
self.assertFalse(hasattr(dude.cars_owned, 'by_name'))
|
|
|
|
|
|
def test_using_dir(self):
|
|
# make sure introspecing via dir() doesn't actually cause queries,
|
|
# just as a sanity check.
|
|
with self.assertNumQueries(0):
|
|
querysets_to_dir = (
|
|
Dude.objects,
|
|
Dude.objects.by_name('Duder'),
|
|
Dude.objects.all().by_name('Duder'),
|
|
Dude.abiders,
|
|
Dude.abiders.rug_positive(),
|
|
Dude.abiders.all().rug_positive()
|
|
)
|
|
for qs in querysets_to_dir:
|
|
self.assertTrue('by_name' in dir(qs))
|
|
self.assertTrue('abiding' in dir(qs))
|
|
self.assertTrue('rug_positive' in dir(qs))
|
|
self.assertTrue('rug_negative' in dir(qs))
|
|
# some standard qs methods
|
|
self.assertTrue('count' in dir(qs))
|
|
self.assertTrue('order_by' in dir(qs))
|
|
self.assertTrue('select_related' in dir(qs))
|
|
# make sure it's been de-duplicated
|
|
self.assertEqual(1, dir(qs).count('distinct'))
|
|
|
|
# manager only method.
|
|
self.assertTrue('get_stats' in dir(Dude.abiders))
|
|
# manager only method shouldn't appear on the non AbidingManager
|
|
self.assertFalse('get_stats' in dir(Dude.objects))
|
|
# standard manager methods
|
|
self.assertTrue('get_query_set' in dir(Dude.abiders))
|
|
self.assertTrue('contribute_to_class' in dir(Dude.abiders))
|
|
|
|
|
|
|
|
class CreatePassThroughManagerTests(TestCase):
|
|
def setUp(self):
|
|
self.dude = Dude.objects.create(name='El Duderino')
|
|
self.other_dude = Dude.objects.create(name='Das Dude')
|
|
|
|
def test_reverse_manager(self):
|
|
Spot.objects.create(
|
|
name='The Crib', owner=self.dude, closed=True, secure=True,
|
|
secret=False)
|
|
self.assertEqual(self.dude.spots_owned.closed().count(), 1)
|
|
Spot.objects.create(
|
|
name='The Crux', owner=self.other_dude, closed=True, secure=True,
|
|
secret=False
|
|
)
|
|
self.assertEqual(self.dude.spots_owned.closed().all().count(), 1)
|
|
self.assertEqual(self.dude.spots_owned.closed().count(), 1)
|
|
|
|
def test_related_queryset_pickling(self):
|
|
Spot.objects.create(
|
|
name='The Crib', owner=self.dude, closed=True, secure=True,
|
|
secret=False)
|
|
qs = self.dude.spots_owned.closed()
|
|
pickled_qs = pickle.dumps(qs)
|
|
unpickled_qs = pickle.loads(pickled_qs)
|
|
self.assertEqual(unpickled_qs.secured().count(), 1)
|
|
|
|
def test_related_queryset_superclass_method(self):
|
|
Spot.objects.create(
|
|
name='The Crib', owner=self.dude, closed=True, secure=True,
|
|
secret=False)
|
|
Spot.objects.create(
|
|
name='The Secret Crib', owner=self.dude, closed=False, secure=True,
|
|
secret=True)
|
|
self.assertEqual(self.dude.spots_owned.count(), 1)
|
|
|
|
def test_related_manager_create(self):
|
|
self.dude.spots_owned.create(name='The Crib', closed=True, secure=True)
|
|
|
|
|
|
class FieldTrackerTestCase(TestCase):
|
|
|
|
tracker = None
|
|
|
|
def assertHasChanged(self, **kwargs):
|
|
tracker = kwargs.pop('tracker', self.tracker)
|
|
for field, value in kwargs.items():
|
|
if value is None:
|
|
with self.assertRaises(FieldError):
|
|
tracker.has_changed(field)
|
|
else:
|
|
self.assertEqual(tracker.has_changed(field), value)
|
|
|
|
def assertPrevious(self, **kwargs):
|
|
tracker = kwargs.pop('tracker', self.tracker)
|
|
for field, value in kwargs.items():
|
|
self.assertEqual(tracker.previous(field), value)
|
|
|
|
def assertChanged(self, **kwargs):
|
|
tracker = kwargs.pop('tracker', self.tracker)
|
|
self.assertEqual(tracker.changed(), kwargs)
|
|
|
|
def assertCurrent(self, **kwargs):
|
|
tracker = kwargs.pop('tracker', self.tracker)
|
|
self.assertEqual(tracker.current(), kwargs)
|
|
|
|
def update_instance(self, **kwargs):
|
|
for field, value in kwargs.items():
|
|
setattr(self.instance, field, value)
|
|
self.instance.save()
|
|
|
|
|
|
class FieldTrackerCommonTests(object):
|
|
|
|
def test_pre_save_previous(self):
|
|
self.assertPrevious(name=None, number=None)
|
|
self.instance.name = 'new age'
|
|
self.instance.number = 8
|
|
self.assertPrevious(name=None, number=None)
|
|
|
|
|
|
class FieldTrackerTests(FieldTrackerTestCase, FieldTrackerCommonTests):
|
|
|
|
tracked_class = Tracked
|
|
|
|
def setUp(self):
|
|
self.instance = self.tracked_class()
|
|
self.tracker = self.instance.tracker
|
|
|
|
def test_descriptor(self):
|
|
self.assertTrue(isinstance(self.tracked_class.tracker, FieldTracker))
|
|
|
|
def test_pre_save_changed(self):
|
|
self.assertChanged(name=None)
|
|
self.instance.name = 'new age'
|
|
self.assertChanged(name=None)
|
|
self.instance.number = 8
|
|
self.assertChanged(name=None, number=None)
|
|
self.instance.name = ''
|
|
self.assertChanged(name=None, number=None)
|
|
self.instance.mutable = [1,2,3]
|
|
self.assertChanged(name=None, number=None, mutable=None)
|
|
|
|
def test_pre_save_has_changed(self):
|
|
self.assertHasChanged(name=True, number=False, mutable=False)
|
|
self.instance.name = 'new age'
|
|
self.assertHasChanged(name=True, number=False, mutable=False)
|
|
self.instance.number = 7
|
|
self.assertHasChanged(name=True, number=True)
|
|
self.instance.mutable = [1,2,3]
|
|
self.assertHasChanged(name=True, number=True, mutable=True)
|
|
|
|
def test_first_save(self):
|
|
self.assertHasChanged(name=True, number=False, mutable=False)
|
|
self.assertPrevious(name=None, number=None, mutable=None)
|
|
self.assertCurrent(name='', number=None, id=None, mutable=None)
|
|
self.assertChanged(name=None)
|
|
self.instance.name = 'retro'
|
|
self.instance.number = 4
|
|
self.instance.mutable = [1,2,3]
|
|
self.assertHasChanged(name=True, number=True, mutable=True)
|
|
self.assertPrevious(name=None, number=None, mutable=None)
|
|
self.assertCurrent(name='retro', number=4, id=None, mutable=[1,2,3])
|
|
self.assertChanged(name=None, number=None, mutable=None)
|
|
# Django 1.4 doesn't have update_fields
|
|
if django.VERSION >= (1, 5, 0):
|
|
self.instance.save(update_fields=[])
|
|
self.assertHasChanged(name=True, number=True, mutable=True)
|
|
self.assertPrevious(name=None, number=None, mutable=None)
|
|
self.assertCurrent(name='retro', number=4, id=None, mutable=[1,2,3])
|
|
self.assertChanged(name=None, number=None, mutable=None)
|
|
with self.assertRaises(ValueError):
|
|
self.instance.save(update_fields=['number'])
|
|
|
|
def test_post_save_has_changed(self):
|
|
self.update_instance(name='retro', number=4, mutable=[1,2,3])
|
|
self.assertHasChanged(name=False, number=False, mutable=False)
|
|
self.instance.name = 'new age'
|
|
self.assertHasChanged(name=True, number=False)
|
|
self.instance.number = 8
|
|
self.assertHasChanged(name=True, number=True)
|
|
self.instance.mutable[1] = 4
|
|
self.assertHasChanged(name=True, number=True, mutable=True)
|
|
self.instance.name = 'retro'
|
|
self.assertHasChanged(name=False, number=True, mutable=True)
|
|
|
|
def test_post_save_previous(self):
|
|
self.update_instance(name='retro', number=4, mutable=[1,2,3])
|
|
self.instance.name = 'new age'
|
|
self.assertPrevious(name='retro', number=4, mutable=[1,2,3])
|
|
self.instance.mutable[1] = 4
|
|
self.assertPrevious(name='retro', number=4, mutable=[1,2,3])
|
|
|
|
def test_post_save_changed(self):
|
|
self.update_instance(name='retro', number=4, mutable=[1,2,3])
|
|
self.assertChanged()
|
|
self.instance.name = 'new age'
|
|
self.assertChanged(name='retro')
|
|
self.instance.number = 8
|
|
self.assertChanged(name='retro', number=4)
|
|
self.instance.name = 'retro'
|
|
self.assertChanged(number=4)
|
|
self.instance.mutable[1] = 4
|
|
self.assertChanged(number=4, mutable=[1,2,3])
|
|
self.instance.mutable = [1,2,3]
|
|
self.assertChanged(number=4)
|
|
|
|
def test_current(self):
|
|
self.assertCurrent(id=None, name='', number=None, mutable=None)
|
|
self.instance.name = 'new age'
|
|
self.assertCurrent(id=None, name='new age', number=None, mutable=None)
|
|
self.instance.number = 8
|
|
self.assertCurrent(id=None, name='new age', number=8, mutable=None)
|
|
self.instance.mutable = [1,2,3]
|
|
self.assertCurrent(id=None, name='new age', number=8, mutable=[1,2,3])
|
|
self.instance.mutable[1] = 4
|
|
self.assertCurrent(id=None, name='new age', number=8, mutable=[1,4,3])
|
|
self.instance.save()
|
|
self.assertCurrent(id=self.instance.id, name='new age', number=8, mutable=[1,4,3])
|
|
|
|
@skipUnless(
|
|
django.VERSION >= (1, 5, 0), "Django 1.4 doesn't have update_fields")
|
|
def test_update_fields(self):
|
|
self.update_instance(name='retro', number=4, mutable=[1,2,3])
|
|
self.assertChanged()
|
|
self.instance.name = 'new age'
|
|
self.instance.number = 8
|
|
self.instance.mutable = [4,5,6]
|
|
self.assertChanged(name='retro', number=4, mutable=[1,2,3])
|
|
self.instance.save(update_fields=[])
|
|
self.assertChanged(name='retro', number=4, mutable=[1,2,3])
|
|
self.instance.save(update_fields=['name'])
|
|
in_db = self.tracked_class.objects.get(id=self.instance.id)
|
|
self.assertEqual(in_db.name, self.instance.name)
|
|
self.assertNotEqual(in_db.number, self.instance.number)
|
|
self.assertChanged(number=4, mutable=[1,2,3])
|
|
self.instance.save(update_fields=['number'])
|
|
self.assertChanged(mutable=[1,2,3])
|
|
self.instance.save(update_fields=['mutable'])
|
|
self.assertChanged()
|
|
in_db = self.tracked_class.objects.get(id=self.instance.id)
|
|
self.assertEqual(in_db.name, self.instance.name)
|
|
self.assertEqual(in_db.number, self.instance.number)
|
|
self.assertEqual(in_db.mutable, self.instance.mutable)
|
|
|
|
def test_with_deferred(self):
|
|
self.instance.name = 'new age'
|
|
self.instance.number = 1
|
|
self.instance.save()
|
|
item = list(self.tracked_class.objects.only('name').all())[0]
|
|
self.assertTrue(item.tracker.deferred_fields)
|
|
|
|
self.assertEqual(item.tracker.previous('number'), None)
|
|
self.assertTrue('number' in item.tracker.deferred_fields)
|
|
|
|
self.assertEqual(item.number, 1)
|
|
self.assertTrue('number' not in item.tracker.deferred_fields)
|
|
self.assertEqual(item.tracker.previous('number'), 1)
|
|
self.assertFalse(item.tracker.has_changed('number'))
|
|
|
|
item.number = 2
|
|
self.assertTrue(item.tracker.has_changed('number'))
|
|
|
|
def test_can_pickle_objects(self):
|
|
pickle.dumps(self.instance)
|
|
|
|
|
|
class FieldTrackedModelCustomTests(FieldTrackerTestCase,
|
|
FieldTrackerCommonTests):
|
|
|
|
tracked_class = TrackedNotDefault
|
|
|
|
def setUp(self):
|
|
self.instance = self.tracked_class()
|
|
self.tracker = self.instance.name_tracker
|
|
|
|
def test_pre_save_changed(self):
|
|
self.assertChanged(name=None)
|
|
self.instance.name = 'new age'
|
|
self.assertChanged(name=None)
|
|
self.instance.number = 8
|
|
self.assertChanged(name=None)
|
|
self.instance.name = ''
|
|
self.assertChanged(name=None)
|
|
|
|
def test_first_save(self):
|
|
self.assertHasChanged(name=True, number=None)
|
|
self.assertPrevious(name=None, number=None)
|
|
self.assertCurrent(name='')
|
|
self.assertChanged(name=None)
|
|
self.instance.name = 'retro'
|
|
self.instance.number = 4
|
|
self.assertHasChanged(name=True, number=None)
|
|
self.assertPrevious(name=None, number=None)
|
|
self.assertCurrent(name='retro')
|
|
self.assertChanged(name=None)
|
|
|
|
def test_pre_save_has_changed(self):
|
|
self.assertHasChanged(name=True, number=None)
|
|
self.instance.name = 'new age'
|
|
self.assertHasChanged(name=True, number=None)
|
|
self.instance.number = 7
|
|
self.assertHasChanged(name=True, number=None)
|
|
|
|
def test_post_save_has_changed(self):
|
|
self.update_instance(name='retro', number=4)
|
|
self.assertHasChanged(name=False, number=None)
|
|
self.instance.name = 'new age'
|
|
self.assertHasChanged(name=True, number=None)
|
|
self.instance.number = 8
|
|
self.assertHasChanged(name=True, number=None)
|
|
self.instance.name = 'retro'
|
|
self.assertHasChanged(name=False, number=None)
|
|
|
|
def test_post_save_previous(self):
|
|
self.update_instance(name='retro', number=4)
|
|
self.instance.name = 'new age'
|
|
self.assertPrevious(name='retro', number=None)
|
|
|
|
def test_post_save_changed(self):
|
|
self.update_instance(name='retro', number=4)
|
|
self.assertChanged()
|
|
self.instance.name = 'new age'
|
|
self.assertChanged(name='retro')
|
|
self.instance.number = 8
|
|
self.assertChanged(name='retro')
|
|
self.instance.name = 'retro'
|
|
self.assertChanged()
|
|
|
|
def test_current(self):
|
|
self.assertCurrent(name='')
|
|
self.instance.name = 'new age'
|
|
self.assertCurrent(name='new age')
|
|
self.instance.number = 8
|
|
self.assertCurrent(name='new age')
|
|
self.instance.save()
|
|
self.assertCurrent(name='new age')
|
|
|
|
@skipUnless(
|
|
django.VERSION >= (1, 5, 0), "Django 1.4 doesn't have update_fields")
|
|
def test_update_fields(self):
|
|
self.update_instance(name='retro', number=4)
|
|
self.assertChanged()
|
|
self.instance.name = 'new age'
|
|
self.instance.number = 8
|
|
self.instance.save(update_fields=['name', 'number'])
|
|
self.assertChanged()
|
|
|
|
|
|
class FieldTrackedModelAttributeTests(FieldTrackerTestCase):
|
|
|
|
tracked_class = TrackedNonFieldAttr
|
|
|
|
def setUp(self):
|
|
self.instance = self.tracked_class()
|
|
self.tracker = self.instance.tracker
|
|
|
|
def test_previous(self):
|
|
self.assertPrevious(rounded=None)
|
|
self.instance.number = 7.5
|
|
self.assertPrevious(rounded=None)
|
|
self.instance.save()
|
|
self.assertPrevious(rounded=8)
|
|
self.instance.number = 7.2
|
|
self.assertPrevious(rounded=8)
|
|
self.instance.save()
|
|
self.assertPrevious(rounded=7)
|
|
|
|
def test_has_changed(self):
|
|
self.assertHasChanged(rounded=False)
|
|
self.instance.number = 7.5
|
|
self.assertHasChanged(rounded=True)
|
|
self.instance.save()
|
|
self.assertHasChanged(rounded=False)
|
|
self.instance.number = 7.2
|
|
self.assertHasChanged(rounded=True)
|
|
self.instance.number = 7.8
|
|
self.assertHasChanged(rounded=False)
|
|
|
|
def test_changed(self):
|
|
self.assertChanged()
|
|
self.instance.number = 7.5
|
|
self.assertPrevious(rounded=None)
|
|
self.instance.save()
|
|
self.assertPrevious()
|
|
self.instance.number = 7.8
|
|
self.assertPrevious()
|
|
self.instance.number = 7.2
|
|
self.assertPrevious(rounded=8)
|
|
self.instance.save()
|
|
self.assertPrevious()
|
|
|
|
def test_current(self):
|
|
self.assertCurrent(rounded=None)
|
|
self.instance.number = 7.5
|
|
self.assertCurrent(rounded=8)
|
|
self.instance.save()
|
|
self.assertCurrent(rounded=8)
|
|
|
|
|
|
class FieldTrackedModelMultiTests(FieldTrackerTestCase,
|
|
FieldTrackerCommonTests):
|
|
|
|
tracked_class = TrackedMultiple
|
|
|
|
def setUp(self):
|
|
self.instance = self.tracked_class()
|
|
self.trackers = [self.instance.name_tracker,
|
|
self.instance.number_tracker]
|
|
|
|
def test_pre_save_changed(self):
|
|
self.tracker = self.instance.name_tracker
|
|
self.assertChanged(name=None)
|
|
self.instance.name = 'new age'
|
|
self.assertChanged(name=None)
|
|
self.instance.number = 8
|
|
self.assertChanged(name=None)
|
|
self.instance.name = ''
|
|
self.assertChanged(name=None)
|
|
self.tracker = self.instance.number_tracker
|
|
self.assertChanged(number=None)
|
|
self.instance.name = 'new age'
|
|
self.assertChanged(number=None)
|
|
self.instance.number = 8
|
|
self.assertChanged(number=None)
|
|
|
|
def test_pre_save_has_changed(self):
|
|
self.tracker = self.instance.name_tracker
|
|
self.assertHasChanged(name=True, number=None)
|
|
self.instance.name = 'new age'
|
|
self.assertHasChanged(name=True, number=None)
|
|
self.tracker = self.instance.number_tracker
|
|
self.assertHasChanged(name=None, number=False)
|
|
self.instance.name = 'new age'
|
|
self.assertHasChanged(name=None, number=False)
|
|
|
|
def test_pre_save_previous(self):
|
|
for tracker in self.trackers:
|
|
self.tracker = tracker
|
|
super(FieldTrackedModelMultiTests, self).test_pre_save_previous()
|
|
|
|
def test_post_save_has_changed(self):
|
|
self.update_instance(name='retro', number=4)
|
|
self.assertHasChanged(tracker=self.trackers[0], name=False, number=None)
|
|
self.assertHasChanged(tracker=self.trackers[1], name=None, number=False)
|
|
self.instance.name = 'new age'
|
|
self.assertHasChanged(tracker=self.trackers[0], name=True, number=None)
|
|
self.assertHasChanged(tracker=self.trackers[1], name=None, number=False)
|
|
self.instance.number = 8
|
|
self.assertHasChanged(tracker=self.trackers[0], name=True, number=None)
|
|
self.assertHasChanged(tracker=self.trackers[1], name=None, number=True)
|
|
self.instance.name = 'retro'
|
|
self.instance.number = 4
|
|
self.assertHasChanged(tracker=self.trackers[0], name=False, number=None)
|
|
self.assertHasChanged(tracker=self.trackers[1], name=None, number=False)
|
|
|
|
def test_post_save_previous(self):
|
|
self.update_instance(name='retro', number=4)
|
|
self.instance.name = 'new age'
|
|
self.instance.number = 8
|
|
self.assertPrevious(tracker=self.trackers[0], name='retro', number=None)
|
|
self.assertPrevious(tracker=self.trackers[1], name=None, number=4)
|
|
|
|
def test_post_save_changed(self):
|
|
self.update_instance(name='retro', number=4)
|
|
self.assertChanged(tracker=self.trackers[0])
|
|
self.assertChanged(tracker=self.trackers[1])
|
|
self.instance.name = 'new age'
|
|
self.assertChanged(tracker=self.trackers[0], name='retro')
|
|
self.assertChanged(tracker=self.trackers[1])
|
|
self.instance.number = 8
|
|
self.assertChanged(tracker=self.trackers[0], name='retro')
|
|
self.assertChanged(tracker=self.trackers[1], number=4)
|
|
self.instance.name = 'retro'
|
|
self.instance.number = 4
|
|
self.assertChanged(tracker=self.trackers[0])
|
|
self.assertChanged(tracker=self.trackers[1])
|
|
|
|
def test_current(self):
|
|
self.assertCurrent(tracker=self.trackers[0], name='')
|
|
self.assertCurrent(tracker=self.trackers[1], number=None)
|
|
self.instance.name = 'new age'
|
|
self.assertCurrent(tracker=self.trackers[0], name='new age')
|
|
self.assertCurrent(tracker=self.trackers[1], number=None)
|
|
self.instance.number = 8
|
|
self.assertCurrent(tracker=self.trackers[0], name='new age')
|
|
self.assertCurrent(tracker=self.trackers[1], number=8)
|
|
self.instance.save()
|
|
self.assertCurrent(tracker=self.trackers[0], name='new age')
|
|
self.assertCurrent(tracker=self.trackers[1], number=8)
|
|
|
|
|
|
class FieldTrackerForeignKeyTests(FieldTrackerTestCase):
|
|
|
|
fk_class = Tracked
|
|
tracked_class = TrackedFK
|
|
|
|
def setUp(self):
|
|
self.old_fk = self.fk_class.objects.create(number=8)
|
|
self.instance = self.tracked_class.objects.create(fk=self.old_fk)
|
|
|
|
def test_default(self):
|
|
self.tracker = self.instance.tracker
|
|
self.assertChanged()
|
|
self.assertPrevious()
|
|
self.assertCurrent(id=self.instance.id, fk_id=self.old_fk.id)
|
|
self.instance.fk = self.fk_class.objects.create(number=8)
|
|
self.assertChanged(fk_id=self.old_fk.id)
|
|
self.assertPrevious(fk_id=self.old_fk.id)
|
|
self.assertCurrent(id=self.instance.id, fk_id=self.instance.fk_id)
|
|
|
|
def test_custom(self):
|
|
self.tracker = self.instance.custom_tracker
|
|
self.assertChanged()
|
|
self.assertPrevious()
|
|
self.assertCurrent(fk_id=self.old_fk.id)
|
|
self.instance.fk = self.fk_class.objects.create(number=8)
|
|
self.assertChanged(fk_id=self.old_fk.id)
|
|
self.assertPrevious(fk_id=self.old_fk.id)
|
|
self.assertCurrent(fk_id=self.instance.fk_id)
|
|
|
|
def test_custom_without_id(self):
|
|
with self.assertNumQueries(1):
|
|
self.tracked_class.objects.get()
|
|
self.tracker = self.instance.custom_tracker_without_id
|
|
self.assertChanged()
|
|
self.assertPrevious()
|
|
self.assertCurrent(fk=self.old_fk.id)
|
|
self.instance.fk = self.fk_class.objects.create(number=8)
|
|
self.assertChanged(fk=self.old_fk.id)
|
|
self.assertPrevious(fk=self.old_fk.id)
|
|
self.assertCurrent(fk=self.instance.fk_id)
|
|
|
|
|
|
class InheritedFieldTrackerTests(FieldTrackerTests):
|
|
|
|
tracked_class = InheritedTracked
|
|
|
|
def test_child_fields_not_tracked(self):
|
|
self.name2 = 'test'
|
|
self.assertEqual(self.tracker.previous('name2'), None)
|
|
self.assertRaises(FieldError, self.tracker.has_changed, 'name2')
|
|
|
|
|
|
class ModelTrackerTests(FieldTrackerTests):
|
|
|
|
tracked_class = ModelTracked
|
|
|
|
def test_pre_save_changed(self):
|
|
self.assertChanged()
|
|
self.instance.name = 'new age'
|
|
self.assertChanged()
|
|
self.instance.number = 8
|
|
self.assertChanged()
|
|
self.instance.name = ''
|
|
self.assertChanged()
|
|
self.instance.mutable = [1,2,3]
|
|
self.assertChanged()
|
|
|
|
def test_first_save(self):
|
|
self.assertHasChanged(name=True, number=True, mutable=True)
|
|
self.assertPrevious(name=None, number=None, mutable=None)
|
|
self.assertCurrent(name='', number=None, id=None, mutable=None)
|
|
self.assertChanged()
|
|
self.instance.name = 'retro'
|
|
self.instance.number = 4
|
|
self.instance.mutable = [1,2,3]
|
|
self.assertHasChanged(name=True, number=True, mutable=True)
|
|
self.assertPrevious(name=None, number=None, mutable=None)
|
|
self.assertCurrent(name='retro', number=4, id=None, mutable=[1,2,3])
|
|
self.assertChanged()
|
|
# Django 1.4 doesn't have update_fields
|
|
if django.VERSION >= (1, 5, 0):
|
|
self.instance.save(update_fields=[])
|
|
self.assertHasChanged(name=True, number=True, mutable=True)
|
|
self.assertPrevious(name=None, number=None, mutable=None)
|
|
self.assertCurrent(name='retro', number=4, id=None, mutable=[1,2,3])
|
|
self.assertChanged()
|
|
with self.assertRaises(ValueError):
|
|
self.instance.save(update_fields=['number'])
|
|
|
|
def test_pre_save_has_changed(self):
|
|
self.assertHasChanged(name=True, number=True)
|
|
self.instance.name = 'new age'
|
|
self.assertHasChanged(name=True, number=True)
|
|
self.instance.number = 7
|
|
self.assertHasChanged(name=True, number=True)
|
|
|
|
|
|
class ModelTrackedModelCustomTests(FieldTrackedModelCustomTests):
|
|
|
|
tracked_class = ModelTrackedNotDefault
|
|
|
|
def test_first_save(self):
|
|
self.assertHasChanged(name=True, number=True)
|
|
self.assertPrevious(name=None, number=None)
|
|
self.assertCurrent(name='')
|
|
self.assertChanged()
|
|
self.instance.name = 'retro'
|
|
self.instance.number = 4
|
|
self.assertHasChanged(name=True, number=True)
|
|
self.assertPrevious(name=None, number=None)
|
|
self.assertCurrent(name='retro')
|
|
self.assertChanged()
|
|
|
|
def test_pre_save_has_changed(self):
|
|
self.assertHasChanged(name=True, number=True)
|
|
self.instance.name = 'new age'
|
|
self.assertHasChanged(name=True, number=True)
|
|
self.instance.number = 7
|
|
self.assertHasChanged(name=True, number=True)
|
|
|
|
def test_pre_save_changed(self):
|
|
self.assertChanged()
|
|
self.instance.name = 'new age'
|
|
self.assertChanged()
|
|
self.instance.number = 8
|
|
self.assertChanged()
|
|
self.instance.name = ''
|
|
self.assertChanged()
|
|
|
|
|
|
class ModelTrackedModelMultiTests(FieldTrackedModelMultiTests):
|
|
|
|
tracked_class = ModelTrackedMultiple
|
|
|
|
def test_pre_save_has_changed(self):
|
|
self.tracker = self.instance.name_tracker
|
|
self.assertHasChanged(name=True, number=True)
|
|
self.instance.name = 'new age'
|
|
self.assertHasChanged(name=True, number=True)
|
|
self.tracker = self.instance.number_tracker
|
|
self.assertHasChanged(name=True, number=True)
|
|
self.instance.name = 'new age'
|
|
self.assertHasChanged(name=True, number=True)
|
|
|
|
def test_pre_save_changed(self):
|
|
self.tracker = self.instance.name_tracker
|
|
self.assertChanged()
|
|
self.instance.name = 'new age'
|
|
self.assertChanged()
|
|
self.instance.number = 8
|
|
self.assertChanged()
|
|
self.instance.name = ''
|
|
self.assertChanged()
|
|
self.tracker = self.instance.number_tracker
|
|
self.assertChanged()
|
|
self.instance.name = 'new age'
|
|
self.assertChanged()
|
|
self.instance.number = 8
|
|
self.assertChanged()
|
|
|
|
|
|
class ModelTrackerForeignKeyTests(FieldTrackerForeignKeyTests):
|
|
|
|
fk_class = ModelTracked
|
|
tracked_class = ModelTrackedFK
|
|
|
|
def test_custom_without_id(self):
|
|
with self.assertNumQueries(2):
|
|
self.tracked_class.objects.get()
|
|
self.tracker = self.instance.custom_tracker_without_id
|
|
self.assertChanged()
|
|
self.assertPrevious()
|
|
self.assertCurrent(fk=self.old_fk)
|
|
self.instance.fk = self.fk_class.objects.create(number=8)
|
|
self.assertNotEqual(self.instance.fk, self.old_fk)
|
|
self.assertChanged(fk=self.old_fk)
|
|
self.assertPrevious(fk=self.old_fk)
|
|
self.assertCurrent(fk=self.instance.fk)
|
|
|
|
|
|
class InheritedModelTrackerTests(ModelTrackerTests):
|
|
|
|
tracked_class = InheritedModelTracked
|
|
|
|
def test_child_fields_not_tracked(self):
|
|
self.name2 = 'test'
|
|
self.assertEqual(self.tracker.previous('name2'), None)
|
|
self.assertTrue(self.tracker.has_changed('name2'))
|