from unittest import skipUnless from django.db import connection from django.db.models import CharField, TextField, Value as V from django.db.models.functions import Concat, ConcatPair, Upper from django.test import TestCase from django.utils import timezone from ..models import Article, Author lorem_ipsum = """ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.""" class ConcatTests(TestCase): def test_basic(self): Author.objects.create(name='Jayden') Author.objects.create(name='John Smith', alias='smithj', goes_by='John') Author.objects.create(name='Margaret', goes_by='Maggie') Author.objects.create(name='Rhonda', alias='adnohR') authors = Author.objects.annotate(joined=Concat('alias', 'goes_by')) self.assertQuerysetEqual( authors.order_by('name'), [ '', 'smithjJohn', 'Maggie', 'adnohR', ], lambda a: a.joined ) def test_gt_two_expressions(self): with self.assertRaisesMessage(ValueError, 'Concat must take at least two expressions'): Author.objects.annotate(joined=Concat('alias')) def test_many(self): Author.objects.create(name='Jayden') Author.objects.create(name='John Smith', alias='smithj', goes_by='John') Author.objects.create(name='Margaret', goes_by='Maggie') Author.objects.create(name='Rhonda', alias='adnohR') authors = Author.objects.annotate( joined=Concat('name', V(' ('), 'goes_by', V(')'), output_field=CharField()), ) self.assertQuerysetEqual( authors.order_by('name'), [ 'Jayden ()', 'John Smith (John)', 'Margaret (Maggie)', 'Rhonda ()', ], lambda a: a.joined ) def test_mixed_char_text(self): Article.objects.create(title='The Title', text=lorem_ipsum, written=timezone.now()) article = Article.objects.annotate( title_text=Concat('title', V(' - '), 'text', output_field=TextField()), ).get(title='The Title') self.assertEqual(article.title + ' - ' + article.text, article.title_text) # Wrap the concat in something else to ensure that text is returned # rather than bytes. article = Article.objects.annotate( title_text=Upper(Concat('title', V(' - '), 'text', output_field=TextField())), ).get(title='The Title') expected = article.title + ' - ' + article.text self.assertEqual(expected.upper(), article.title_text) @skipUnless(connection.vendor == 'sqlite', "sqlite specific implementation detail.") def test_coalesce_idempotent(self): pair = ConcatPair(V('a'), V('b')) # Check nodes counts self.assertEqual(len(list(pair.flatten())), 3) self.assertEqual(len(list(pair.coalesce().flatten())), 7) # + 2 Coalesce + 2 Value() self.assertEqual(len(list(pair.flatten())), 3) def test_sql_generation_idempotency(self): qs = Article.objects.annotate(description=Concat('title', V(': '), 'summary')) # Multiple compilations should not alter the generated query. self.assertEqual(str(qs.query), str(qs.all().query))