diff options
| author | Ian Foote <python@ian.feete.org> | 2015-05-09 12:55:03 +0100 |
|---|---|---|
| committer | Marc Tamlyn <marc.tamlyn@gmail.com> | 2015-06-05 11:15:33 +0100 |
| commit | 4ab53a558ac01c2dd7dafb8350cd72c630372335 (patch) | |
| tree | a487e54441bcb9abad405139a1e88dbe2f952a56 /tests | |
| parent | fe21fb810a1bd12b10c534923809423b5c1cf4d7 (diff) | |
Fixed #24767 -- Added Greatest and Least expressions
Greatest and Least are row-level Function versions of Min and Max.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/db_functions/models.py | 12 | ||||
| -rw-r--r-- | tests/db_functions/tests.py | 199 |
2 files changed, 208 insertions, 3 deletions
diff --git a/tests/db_functions/models.py b/tests/db_functions/models.py index 19722aeed2..f100b79ff5 100644 --- a/tests/db_functions/models.py +++ b/tests/db_functions/models.py @@ -12,6 +12,7 @@ class Author(models.Model): name = models.CharField(max_length=50) alias = models.CharField(max_length=50, null=True, blank=True) goes_by = models.CharField(max_length=50, null=True, blank=True) + age = models.PositiveSmallIntegerField(default=30) def __str__(self): return self.name @@ -25,7 +26,18 @@ class Article(models.Model): text = models.TextField() written = models.DateTimeField() published = models.DateTimeField(null=True, blank=True) + updated = models.DateTimeField(null=True, blank=True) views = models.PositiveIntegerField(default=0) def __str__(self): return self.title + + +@python_2_unicode_compatible +class Fan(models.Model): + name = models.CharField(max_length=50) + age = models.PositiveSmallIntegerField(default=30) + author = models.ForeignKey(Author, related_name='fans') + + def __str__(self): + return self.name diff --git a/tests/db_functions/tests.py b/tests/db_functions/tests.py index 1df9800f13..8d465b1bd3 100644 --- a/tests/db_functions/tests.py +++ b/tests/db_functions/tests.py @@ -1,15 +1,18 @@ from __future__ import unicode_literals from datetime import datetime, timedelta +from unittest import skipIf, skipUnless +from django.db import connection from django.db.models import CharField, TextField, Value as V +from django.db.models.expressions import RawSQL from django.db.models.functions import ( - Coalesce, Concat, Length, Lower, Now, Substr, Upper, + Coalesce, Concat, Greatest, Least, Length, Lower, Now, Substr, Upper, ) -from django.test import TestCase +from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature from django.utils import six, timezone -from .models import Article, Author +from .models import Article, Author, Fan lorem_ipsum = """ @@ -101,6 +104,196 @@ class FunctionTests(TestCase): lambda a: a.name ) + def test_greatest(self): + now = timezone.now() + before = now - timedelta(hours=1) + + Article.objects.create( + title="Testing with Django", + written=before, + published=now, + ) + + articles = Article.objects.annotate( + last_updated=Greatest('written', 'published'), + ) + self.assertEqual(articles.first().last_updated, now) + + @skipUnlessDBFeature('greatest_least_ignores_nulls') + def test_greatest_ignores_null(self): + now = timezone.now() + + Article.objects.create(title="Testing with Django", written=now) + + articles = Article.objects.annotate( + last_updated=Greatest('written', 'published'), + ) + self.assertEqual(articles.first().last_updated, now) + + @skipIfDBFeature('greatest_least_ignores_nulls') + def test_greatest_propogates_null(self): + now = timezone.now() + + Article.objects.create(title="Testing with Django", written=now) + + articles = Article.objects.annotate( + last_updated=Greatest('written', 'published'), + ) + self.assertIsNone(articles.first().last_updated) + + @skipIf(connection.vendor == 'mysql', "This doesn't work on MySQL") + def test_greatest_coalesce_workaround(self): + past = datetime(1900, 1, 1) + now = timezone.now() + + Article.objects.create(title="Testing with Django", written=now) + + articles = Article.objects.annotate( + last_updated=Greatest( + Coalesce('written', past), + Coalesce('published', past), + ), + ) + self.assertEqual(articles.first().last_updated, now) + + @skipUnless(connection.vendor == 'mysql', "MySQL-specific workaround") + def test_greatest_coalesce_workaround_mysql(self): + past = datetime(1900, 1, 1) + now = timezone.now() + + Article.objects.create(title="Testing with Django", written=now) + + past_sql = RawSQL("cast(%s as datetime)", (past,)) + articles = Article.objects.annotate( + last_updated=Greatest( + Coalesce('written', past_sql), + Coalesce('published', past_sql), + ), + ) + self.assertEqual(articles.first().last_updated, now) + + def test_greatest_all_null(self): + Article.objects.create(title="Testing with Django", written=timezone.now()) + + articles = Article.objects.annotate(last_updated=Greatest('published', 'updated')) + self.assertIsNone(articles.first().last_updated) + + def test_greatest_one_expressions(self): + with self.assertRaisesMessage(ValueError, 'Greatest must take at least two expressions'): + Greatest('written') + + def test_greatest_related_field(self): + author = Author.objects.create(name='John Smith', age=45) + Fan.objects.create(name='Margaret', age=50, author=author) + + authors = Author.objects.annotate( + highest_age=Greatest('age', 'fans__age'), + ) + self.assertEqual(authors.first().highest_age, 50) + + def test_greatest_update(self): + author = Author.objects.create(name='James Smith', goes_by='Jim') + + Author.objects.update(alias=Greatest('name', 'goes_by')) + + author.refresh_from_db() + self.assertEqual(author.alias, 'Jim') + + def test_least(self): + now = timezone.now() + before = now - timedelta(hours=1) + + Article.objects.create( + title="Testing with Django", + written=before, + published=now, + ) + + articles = Article.objects.annotate( + first_updated=Least('written', 'published'), + ) + self.assertEqual(articles.first().first_updated, before) + + @skipUnlessDBFeature('greatest_least_ignores_nulls') + def test_least_ignores_null(self): + now = timezone.now() + + Article.objects.create(title="Testing with Django", written=now) + + articles = Article.objects.annotate( + first_updated=Least('written', 'published'), + ) + self.assertEqual(articles.first().first_updated, now) + + @skipIfDBFeature('greatest_least_ignores_nulls') + def test_least_propogates_null(self): + now = timezone.now() + + Article.objects.create(title="Testing with Django", written=now) + + articles = Article.objects.annotate( + first_updated=Least('written', 'published'), + ) + self.assertIsNone(articles.first().first_updated) + + @skipIf(connection.vendor == 'mysql', "This doesn't work on MySQL") + def test_least_coalesce_workaround(self): + future = datetime(2100, 1, 1) + now = timezone.now() + + Article.objects.create(title="Testing with Django", written=now) + + articles = Article.objects.annotate( + last_updated=Least( + Coalesce('written', future), + Coalesce('published', future), + ), + ) + self.assertEqual(articles.first().last_updated, now) + + @skipUnless(connection.vendor == 'mysql', "MySQL-specific workaround") + def test_least_coalesce_workaround_mysql(self): + future = datetime(2100, 1, 1) + now = timezone.now() + + Article.objects.create(title="Testing with Django", written=now) + + future_sql = RawSQL("cast(%s as datetime)", (future,)) + articles = Article.objects.annotate( + last_updated=Least( + Coalesce('written', future_sql), + Coalesce('published', future_sql), + ), + ) + self.assertEqual(articles.first().last_updated, now) + + def test_least_all_null(self): + Article.objects.create(title="Testing with Django", written=timezone.now()) + + articles = Article.objects.annotate(first_updated=Least('published', 'updated')) + self.assertIsNone(articles.first().first_updated) + + def test_least_one_expressions(self): + with self.assertRaisesMessage(ValueError, 'Least must take at least two expressions'): + Least('written') + + def test_least_related_field(self): + author = Author.objects.create(name='John Smith', age=45) + Fan.objects.create(name='Margaret', age=50, author=author) + + authors = Author.objects.annotate( + lowest_age=Least('age', 'fans__age'), + ) + self.assertEqual(authors.first().lowest_age, 45) + + def test_least_update(self): + author = Author.objects.create(name='James Smith', goes_by='Jim') + + Author.objects.update(alias=Least('name', 'goes_by')) + + author.refresh_from_db() + self.assertEqual(author.alias, 'James Smith') + def test_concat(self): Author.objects.create(name='Jayden') Author.objects.create(name='John Smith', alias='smithj', goes_by='John') |
