mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #30240 -- Added SHA1, SHA224, SHA256, SHA384, and SHA512 database functions.
Thanks Mariusz Felisiak and Tim Graham for reviews.
This commit is contained in:
		
				
					committed by
					
						 Mariusz Felisiak
						Mariusz Felisiak
					
				
			
			
				
	
			
			
			
						parent
						
							0193bf874f
						
					
				
				
					commit
					0b70985f42
				
			| @@ -226,6 +226,11 @@ class DatabaseWrapper(BaseDatabaseWrapper): | ||||
|         conn.create_function('REPEAT', 2, none_guard(operator.mul)) | ||||
|         conn.create_function('REVERSE', 1, none_guard(lambda x: x[::-1])) | ||||
|         conn.create_function('RPAD', 3, _sqlite_rpad) | ||||
|         conn.create_function('SHA1', 1, none_guard(lambda x: hashlib.sha1(x.encode()).hexdigest())) | ||||
|         conn.create_function('SHA224', 1, none_guard(lambda x: hashlib.sha224(x.encode()).hexdigest())) | ||||
|         conn.create_function('SHA256', 1, none_guard(lambda x: hashlib.sha256(x.encode()).hexdigest())) | ||||
|         conn.create_function('SHA384', 1, none_guard(lambda x: hashlib.sha384(x.encode()).hexdigest())) | ||||
|         conn.create_function('SHA512', 1, none_guard(lambda x: hashlib.sha512(x.encode()).hexdigest())) | ||||
|         conn.create_function('SIN', 1, none_guard(math.sin)) | ||||
|         conn.create_function('SQRT', 1, none_guard(math.sqrt)) | ||||
|         conn.create_function('TAN', 1, none_guard(math.tan)) | ||||
|   | ||||
| @@ -10,9 +10,9 @@ from .math import ( | ||||
|     Mod, Pi, Power, Radians, Round, Sin, Sqrt, Tan, | ||||
| ) | ||||
| from .text import ( | ||||
|     MD5, Chr, Concat, ConcatPair, Left, Length, Lower, LPad, LTrim, Ord, | ||||
|     Repeat, Replace, Reverse, Right, RPad, RTrim, StrIndex, Substr, Trim, | ||||
|     Upper, | ||||
|     MD5, SHA1, SHA224, SHA256, SHA384, SHA512, Chr, Concat, ConcatPair, Left, | ||||
|     Length, Lower, LPad, LTrim, Ord, Repeat, Replace, Reverse, Right, RPad, | ||||
|     RTrim, StrIndex, Substr, Trim, Upper, | ||||
| ) | ||||
| from .window import ( | ||||
|     CumeDist, DenseRank, FirstValue, Lag, LastValue, Lead, NthValue, Ntile, | ||||
| @@ -34,9 +34,10 @@ __all__ = [ | ||||
|     'Exp', 'Floor', 'Ln', 'Log', 'Mod', 'Pi', 'Power', 'Radians', 'Round', | ||||
|     'Sin', 'Sqrt', 'Tan', | ||||
|     # text | ||||
|     'MD5', 'Chr', 'Concat', 'ConcatPair', 'Left', 'Length', 'Lower', 'LPad', | ||||
|     'LTrim', 'Ord', 'Repeat', 'Replace', 'Reverse', 'Right', 'RPad', 'RTrim', | ||||
|     'StrIndex', 'Substr', 'Trim', 'Upper', | ||||
|     'MD5', 'SHA1', 'SHA224', 'SHA256', 'SHA384', 'SHA512', 'Chr', 'Concat', | ||||
|     'ConcatPair', 'Left', 'Length', 'Lower', 'LPad', 'LTrim', 'Ord', 'Repeat', | ||||
|     'Replace', 'Reverse', 'Right', 'RPad', 'RTrim', 'StrIndex', 'Substr', | ||||
|     'Trim', 'Upper', | ||||
|     # window | ||||
|     'CumeDist', 'DenseRank', 'FirstValue', 'Lag', 'LastValue', 'Lead', | ||||
|     'NthValue', 'Ntile', 'PercentRank', 'Rank', 'RowNumber', | ||||
|   | ||||
| @@ -2,6 +2,7 @@ from django.db.models.expressions import Func, Value | ||||
| from django.db.models.fields import IntegerField | ||||
| from django.db.models.functions import Coalesce | ||||
| from django.db.models.lookups import Transform | ||||
| from django.db.utils import NotSupportedError | ||||
|  | ||||
|  | ||||
| class BytesToCharFieldConversionMixin: | ||||
| @@ -20,6 +21,40 @@ class BytesToCharFieldConversionMixin: | ||||
|         return super().convert_value(value, expression, connection) | ||||
|  | ||||
|  | ||||
| class MySQLSHA2Mixin: | ||||
|     def as_mysql(self, compiler, connection, **extra_content): | ||||
|         return super().as_sql( | ||||
|             compiler, | ||||
|             connection, | ||||
|             template='SHA2(%%(expressions)s, %s)' % self.function[3:], | ||||
|             **extra_content, | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class OracleHashMixin: | ||||
|     def as_oracle(self, compiler, connection, **extra_context): | ||||
|         return super().as_sql( | ||||
|             compiler, | ||||
|             connection, | ||||
|             template=( | ||||
|                 "LOWER(RAWTOHEX(STANDARD_HASH(UTL_I18N.STRING_TO_RAW(" | ||||
|                 "%(expressions)s, 'AL32UTF8'), '%(function)s')))" | ||||
|             ), | ||||
|             **extra_context, | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class PostgreSQLSHAMixin: | ||||
|     def as_postgresql(self, compiler, connection, **extra_content): | ||||
|         return super().as_sql( | ||||
|             compiler, | ||||
|             connection, | ||||
|             template="ENCODE(DIGEST(%(expressions)s, '%(function)s'), 'hex')", | ||||
|             function=self.function.lower(), | ||||
|             **extra_content, | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class Chr(Transform): | ||||
|     function = 'CHR' | ||||
|     lookup_name = 'chr' | ||||
| @@ -150,21 +185,10 @@ class LTrim(Transform): | ||||
|     lookup_name = 'ltrim' | ||||
|  | ||||
|  | ||||
| class MD5(Transform): | ||||
| class MD5(OracleHashMixin, Transform): | ||||
|     function = 'MD5' | ||||
|     lookup_name = 'md5' | ||||
|  | ||||
|     def as_oracle(self, compiler, connection, **extra_context): | ||||
|         return super().as_sql( | ||||
|             compiler, | ||||
|             connection, | ||||
|             template=( | ||||
|                 "LOWER(RAWTOHEX(STANDARD_HASH(UTL_I18N.STRING_TO_RAW(" | ||||
|                 "%(expressions)s, 'AL32UTF8'), '%(function)s')))" | ||||
|             ), | ||||
|             **extra_context, | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class Ord(Transform): | ||||
|     function = 'ASCII' | ||||
| @@ -235,6 +259,34 @@ class RTrim(Transform): | ||||
|     lookup_name = 'rtrim' | ||||
|  | ||||
|  | ||||
| class SHA1(OracleHashMixin, PostgreSQLSHAMixin, Transform): | ||||
|     function = 'SHA1' | ||||
|     lookup_name = 'sha1' | ||||
|  | ||||
|  | ||||
| class SHA224(MySQLSHA2Mixin, PostgreSQLSHAMixin, Transform): | ||||
|     function = 'SHA224' | ||||
|     lookup_name = 'sha224' | ||||
|  | ||||
|     def as_oracle(self, compiler, connection, **extra_context): | ||||
|         raise NotSupportedError('SHA224 is not supported on Oracle.') | ||||
|  | ||||
|  | ||||
| class SHA256(MySQLSHA2Mixin, OracleHashMixin, PostgreSQLSHAMixin, Transform): | ||||
|     function = 'SHA256' | ||||
|     lookup_name = 'sha256' | ||||
|  | ||||
|  | ||||
| class SHA384(MySQLSHA2Mixin, OracleHashMixin, PostgreSQLSHAMixin, Transform): | ||||
|     function = 'SHA384' | ||||
|     lookup_name = 'sha384' | ||||
|  | ||||
|  | ||||
| class SHA512(MySQLSHA2Mixin, OracleHashMixin, PostgreSQLSHAMixin, Transform): | ||||
|     function = 'SHA512' | ||||
|     lookup_name = 'sha512' | ||||
|  | ||||
|  | ||||
| class StrIndex(Func): | ||||
|     """ | ||||
|     Return a positive integer corresponding to the 1-indexed position of the | ||||
|   | ||||
| @@ -1441,6 +1441,41 @@ side. | ||||
| Similar to :class:`~django.db.models.functions.Trim`, but removes only trailing | ||||
| spaces. | ||||
|  | ||||
| ``SHA1``, ``SHA224``, ``SHA256``, ``SHA384``, and ``SHA512`` | ||||
| ------------------------------------------------------------ | ||||
|  | ||||
| .. class:: SHA1(expression, **extra) | ||||
| .. class:: SHA224(expression, **extra) | ||||
| .. class:: SHA256(expression, **extra) | ||||
| .. class:: SHA384(expression, **extra) | ||||
| .. class:: SHA512(expression, **extra) | ||||
|  | ||||
| .. versionadded:: 3.0 | ||||
|  | ||||
| Accepts a single text field or expression and returns the particular hash of | ||||
| the string. | ||||
|  | ||||
| They can also be registered as transforms as described in :class:`Length`. | ||||
|  | ||||
| Usage example:: | ||||
|  | ||||
|     >>> from django.db.models.functions import SHA1 | ||||
|     >>> Author.objects.create(name='Margaret Smith') | ||||
|     >>> author = Author.objects.annotate(name_sha1=SHA1('name')).get() | ||||
|     >>> print(author.name_sha1) | ||||
|     b87efd8a6c991c390be5a68e8a7945a7851c7e5c | ||||
|  | ||||
| .. admonition:: PostgreSQL | ||||
|  | ||||
|     The `pgcrypto extension <https://www.postgresql.org/docs/current/static/ | ||||
|     pgcrypto.html>`_ must be installed. You can use the | ||||
|     :class:`~django.contrib.postgres.operations.CryptoExtension` migration | ||||
|     operation to install it. | ||||
|  | ||||
| .. admonition:: Oracle | ||||
|  | ||||
|     Oracle doesn't support the ``SHA224`` function. | ||||
|  | ||||
| ``StrIndex`` | ||||
| ------------ | ||||
|  | ||||
|   | ||||
| @@ -168,7 +168,12 @@ Migrations | ||||
| Models | ||||
| ~~~~~~ | ||||
|  | ||||
| * Added the :class:`~django.db.models.functions.MD5` database function. | ||||
| * Added hash database functions :class:`~django.db.models.functions.MD5`, | ||||
|   :class:`~django.db.models.functions.SHA1`, | ||||
|   :class:`~django.db.models.functions.SHA224`, | ||||
|   :class:`~django.db.models.functions.SHA256`, | ||||
|   :class:`~django.db.models.functions.SHA384`, and | ||||
|   :class:`~django.db.models.functions.SHA512`. | ||||
|  | ||||
| * The new ``is_dst``  parameter of the | ||||
|   :class:`~django.db.models.functions.Trunc` database functions determines the | ||||
|   | ||||
							
								
								
									
										13
									
								
								tests/db_functions/migrations/0001_setup_extensions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								tests/db_functions/migrations/0001_setup_extensions.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| from unittest import mock | ||||
|  | ||||
| from django.db import migrations | ||||
|  | ||||
| try: | ||||
|     from django.contrib.postgres.operations import CryptoExtension | ||||
| except ImportError: | ||||
|     CryptoExtension = mock.Mock() | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|     # Required for the SHA database functions. | ||||
|     operations = [CryptoExtension()] | ||||
							
								
								
									
										77
									
								
								tests/db_functions/migrations/0002_create_test_models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								tests/db_functions/migrations/0002_create_test_models.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('db_functions', '0001_setup_extensions'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.CreateModel( | ||||
|             name='Author', | ||||
|             fields=[ | ||||
|                 ('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)), | ||||
|             ], | ||||
|         ), | ||||
|         migrations.CreateModel( | ||||
|             name='Article', | ||||
|             fields=[ | ||||
|                 ('authors', models.ManyToManyField('db_functions.Author', related_name='articles')), | ||||
|                 ('title', models.CharField(max_length=50)), | ||||
|                 ('summary', models.CharField(max_length=200, null=True, blank=True)), | ||||
|                 ('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)), | ||||
|             ], | ||||
|         ), | ||||
|         migrations.CreateModel( | ||||
|             name='Fan', | ||||
|             fields=[ | ||||
|                 ('name', models.CharField(max_length=50)), | ||||
|                 ('age', models.PositiveSmallIntegerField(default=30)), | ||||
|                 ('author', models.ForeignKey('db_functions.Author', models.CASCADE, related_name='fans')), | ||||
|                 ('fan_since', models.DateTimeField(null=True, blank=True)), | ||||
|             ], | ||||
|         ), | ||||
|         migrations.CreateModel( | ||||
|             name='DTModel', | ||||
|             fields=[ | ||||
|                 ('name', models.CharField(max_length=32)), | ||||
|                 ('start_datetime', models.DateTimeField(null=True, blank=True)), | ||||
|                 ('end_datetime', models.DateTimeField(null=True, blank=True)), | ||||
|                 ('start_date', models.DateField(null=True, blank=True)), | ||||
|                 ('end_date', models.DateField(null=True, blank=True)), | ||||
|                 ('start_time', models.TimeField(null=True, blank=True)), | ||||
|                 ('end_time', models.TimeField(null=True, blank=True)), | ||||
|                 ('duration', models.DurationField(null=True, blank=True)), | ||||
|             ], | ||||
|         ), | ||||
|         migrations.CreateModel( | ||||
|             name='DecimalModel', | ||||
|             fields=[ | ||||
|                 ('n1', models.DecimalField(decimal_places=2, max_digits=6)), | ||||
|                 ('n2', models.DecimalField(decimal_places=2, max_digits=6)), | ||||
|             ], | ||||
|         ), | ||||
|         migrations.CreateModel( | ||||
|             name='IntegerModel', | ||||
|             fields=[ | ||||
|                 ('big', models.BigIntegerField(null=True, blank=True)), | ||||
|                 ('normal', models.IntegerField(null=True, blank=True)), | ||||
|                 ('small', models.SmallIntegerField(null=True, blank=True)), | ||||
|             ], | ||||
|         ), | ||||
|         migrations.CreateModel( | ||||
|             name='FloatModel', | ||||
|             fields=[ | ||||
|                 ('f1', models.FloatField(null=True, blank=True)), | ||||
|                 ('f2', models.FloatField(null=True, blank=True)), | ||||
|             ], | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										0
									
								
								tests/db_functions/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/db_functions/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										42
									
								
								tests/db_functions/text/test_sha1.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								tests/db_functions/text/test_sha1.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| from django.db import connection | ||||
| from django.db.models import CharField | ||||
| from django.db.models.functions import SHA1 | ||||
| from django.test import TestCase | ||||
| from django.test.utils import register_lookup | ||||
|  | ||||
| from ..models import Author | ||||
|  | ||||
|  | ||||
| class SHA1Tests(TestCase): | ||||
|     @classmethod | ||||
|     def setUpTestData(cls): | ||||
|         Author.objects.bulk_create([ | ||||
|             Author(alias='John Smith'), | ||||
|             Author(alias='Jordan Élena'), | ||||
|             Author(alias='皇帝'), | ||||
|             Author(alias=''), | ||||
|             Author(alias=None), | ||||
|         ]) | ||||
|  | ||||
|     def test_basic(self): | ||||
|         authors = Author.objects.annotate( | ||||
|             sha1_alias=SHA1('alias'), | ||||
|         ).values_list('sha1_alias', flat=True).order_by('pk') | ||||
|         self.assertSequenceEqual( | ||||
|             authors, | ||||
|             [ | ||||
|                 'e61a3587b3f7a142b8c7b9263c82f8119398ecb7', | ||||
|                 '0781e0745a2503e6ded05ed5bc554c421d781b0c', | ||||
|                 '198d15ea139de04060caf95bc3e0ec5883cba881', | ||||
|                 'da39a3ee5e6b4b0d3255bfef95601890afd80709', | ||||
|                 'da39a3ee5e6b4b0d3255bfef95601890afd80709' | ||||
|                 if connection.features.interprets_empty_strings_as_nulls else None, | ||||
|             ], | ||||
|         ) | ||||
|  | ||||
|     def test_transform(self): | ||||
|         with register_lookup(CharField, SHA1): | ||||
|             authors = Author.objects.filter( | ||||
|                 alias__sha1='e61a3587b3f7a142b8c7b9263c82f8119398ecb7', | ||||
|             ).values_list('alias', flat=True) | ||||
|             self.assertSequenceEqual(authors, ['John Smith']) | ||||
							
								
								
									
										53
									
								
								tests/db_functions/text/test_sha224.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								tests/db_functions/text/test_sha224.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| import unittest | ||||
|  | ||||
| from django.db import connection | ||||
| from django.db.models import CharField | ||||
| from django.db.models.functions import SHA224 | ||||
| from django.db.utils import NotSupportedError | ||||
| from django.test import TestCase | ||||
| from django.test.utils import register_lookup | ||||
|  | ||||
| from ..models import Author | ||||
|  | ||||
|  | ||||
| class SHA224Tests(TestCase): | ||||
|     @classmethod | ||||
|     def setUpTestData(cls): | ||||
|         Author.objects.bulk_create([ | ||||
|             Author(alias='John Smith'), | ||||
|             Author(alias='Jordan Élena'), | ||||
|             Author(alias='皇帝'), | ||||
|             Author(alias=''), | ||||
|             Author(alias=None), | ||||
|         ]) | ||||
|  | ||||
|     @unittest.skipIf(connection.vendor == 'oracle', "Oracle doesn't support SHA224.") | ||||
|     def test_basic(self): | ||||
|         authors = Author.objects.annotate( | ||||
|             sha224_alias=SHA224('alias'), | ||||
|         ).values_list('sha224_alias', flat=True).order_by('pk') | ||||
|         self.assertSequenceEqual( | ||||
|             authors, | ||||
|             [ | ||||
|                 'a61303c220731168452cb6acf3759438b1523e768f464e3704e12f70', | ||||
|                 '2297904883e78183cb118fc3dc21a610d60daada7b6ebdbc85139f4d', | ||||
|                 'eba942746e5855121d9d8f79e27dfdebed81adc85b6bf41591203080', | ||||
|                 'd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f', | ||||
|                 'd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f' | ||||
|                 if connection.features.interprets_empty_strings_as_nulls else None, | ||||
|             ], | ||||
|         ) | ||||
|  | ||||
|     @unittest.skipIf(connection.vendor == 'oracle', "Oracle doesn't support SHA224.") | ||||
|     def test_transform(self): | ||||
|         with register_lookup(CharField, SHA224): | ||||
|             authors = Author.objects.filter( | ||||
|                 alias__sha224='a61303c220731168452cb6acf3759438b1523e768f464e3704e12f70', | ||||
|             ).values_list('alias', flat=True) | ||||
|             self.assertSequenceEqual(authors, ['John Smith']) | ||||
|  | ||||
|     @unittest.skipUnless(connection.vendor == 'oracle', "Oracle doesn't support SHA224.") | ||||
|     def test_unsupported(self): | ||||
|         msg = 'SHA224 is not supported on Oracle.' | ||||
|         with self.assertRaisesMessage(NotSupportedError, msg): | ||||
|             Author.objects.annotate(sha224_alias=SHA224('alias')).first() | ||||
							
								
								
									
										42
									
								
								tests/db_functions/text/test_sha256.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								tests/db_functions/text/test_sha256.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| from django.db import connection | ||||
| from django.db.models import CharField | ||||
| from django.db.models.functions import SHA256 | ||||
| from django.test import TestCase | ||||
| from django.test.utils import register_lookup | ||||
|  | ||||
| from ..models import Author | ||||
|  | ||||
|  | ||||
| class SHA256Tests(TestCase): | ||||
|     @classmethod | ||||
|     def setUpTestData(cls): | ||||
|         Author.objects.bulk_create([ | ||||
|             Author(alias='John Smith'), | ||||
|             Author(alias='Jordan Élena'), | ||||
|             Author(alias='皇帝'), | ||||
|             Author(alias=''), | ||||
|             Author(alias=None), | ||||
|         ]) | ||||
|  | ||||
|     def test_basic(self): | ||||
|         authors = Author.objects.annotate( | ||||
|             sha256_alias=SHA256('alias'), | ||||
|         ).values_list('sha256_alias', flat=True).order_by('pk') | ||||
|         self.assertSequenceEqual( | ||||
|             authors, | ||||
|             [ | ||||
|                 'ef61a579c907bbed674c0dbcbcf7f7af8f851538eef7b8e58c5bee0b8cfdac4a', | ||||
|                 '6e4cce20cd83fc7c202f21a8b2452a68509cf24d1c272a045b5e0cfc43f0d94e', | ||||
|                 '3ad2039e3ec0c88973ae1c0fce5a3dbafdd5a1627da0a92312c54ebfcf43988e', | ||||
|                 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', | ||||
|                 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' | ||||
|                 if connection.features.interprets_empty_strings_as_nulls else None, | ||||
|             ], | ||||
|         ) | ||||
|  | ||||
|     def test_transform(self): | ||||
|         with register_lookup(CharField, SHA256): | ||||
|             authors = Author.objects.filter( | ||||
|                 alias__sha256='ef61a579c907bbed674c0dbcbcf7f7af8f851538eef7b8e58c5bee0b8cfdac4a', | ||||
|             ).values_list('alias', flat=True) | ||||
|             self.assertSequenceEqual(authors, ['John Smith']) | ||||
							
								
								
									
										44
									
								
								tests/db_functions/text/test_sha384.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								tests/db_functions/text/test_sha384.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| from django.db import connection | ||||
| from django.db.models import CharField | ||||
| from django.db.models.functions import SHA384 | ||||
| from django.test import TestCase | ||||
| from django.test.utils import register_lookup | ||||
|  | ||||
| from ..models import Author | ||||
|  | ||||
|  | ||||
| class SHA384Tests(TestCase): | ||||
|     @classmethod | ||||
|     def setUpTestData(cls): | ||||
|         Author.objects.bulk_create([ | ||||
|             Author(alias='John Smith'), | ||||
|             Author(alias='Jordan Élena'), | ||||
|             Author(alias='皇帝'), | ||||
|             Author(alias=''), | ||||
|             Author(alias=None), | ||||
|         ]) | ||||
|  | ||||
|     def test_basic(self): | ||||
|         authors = Author.objects.annotate( | ||||
|             sha384_alias=SHA384('alias'), | ||||
|         ).values_list('sha384_alias', flat=True).order_by('pk') | ||||
|         self.assertSequenceEqual( | ||||
|             authors, | ||||
|             [ | ||||
|                 '9df976bfbcf96c66fbe5cba866cd4deaa8248806f15b69c4010a404112906e4ca7b57e53b9967b80d77d4f5c2982cbc8', | ||||
|                 '72202c8005492016cc670219cce82d47d6d2d4273464c742ab5811d691b1e82a7489549e3a73ffa119694f90678ba2e3', | ||||
|                 'eda87fae41e59692c36c49e43279c8111a00d79122a282a944e8ba9a403218f049a48326676a43c7ba378621175853b0', | ||||
|                 '38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b', | ||||
|                 '38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b' | ||||
|                 if connection.features.interprets_empty_strings_as_nulls else None, | ||||
|             ], | ||||
|         ) | ||||
|  | ||||
|     def test_transform(self): | ||||
|         with register_lookup(CharField, SHA384): | ||||
|             authors = Author.objects.filter( | ||||
|                 alias__sha384=( | ||||
|                     '9df976bfbcf96c66fbe5cba866cd4deaa8248806f15b69c4010a404112906e4ca7b57e53b9967b80d77d4f5c2982cbc8' | ||||
|                 ), | ||||
|             ).values_list('alias', flat=True) | ||||
|             self.assertSequenceEqual(authors, ['John Smith']) | ||||
							
								
								
									
										51
									
								
								tests/db_functions/text/test_sha512.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								tests/db_functions/text/test_sha512.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| from django.db import connection | ||||
| from django.db.models import CharField | ||||
| from django.db.models.functions import SHA512 | ||||
| from django.test import TestCase | ||||
| from django.test.utils import register_lookup | ||||
|  | ||||
| from ..models import Author | ||||
|  | ||||
|  | ||||
| class SHA512Tests(TestCase): | ||||
|     @classmethod | ||||
|     def setUpTestData(cls): | ||||
|         Author.objects.bulk_create([ | ||||
|             Author(alias='John Smith'), | ||||
|             Author(alias='Jordan Élena'), | ||||
|             Author(alias='皇帝'), | ||||
|             Author(alias=''), | ||||
|             Author(alias=None), | ||||
|         ]) | ||||
|  | ||||
|     def test_basic(self): | ||||
|         authors = Author.objects.annotate( | ||||
|             sha512_alias=SHA512('alias'), | ||||
|         ).values_list('sha512_alias', flat=True).order_by('pk') | ||||
|         self.assertSequenceEqual( | ||||
|             authors, | ||||
|             [ | ||||
|                 'ed014a19bb67a85f9c8b1d81e04a0e7101725be8627d79d02ca4f3bd803f33cf' | ||||
|                 '3b8fed53e80d2a12c0d0e426824d99d110f0919298a5055efff040a3fc091518', | ||||
|                 'b09c449f3ba49a32ab44754982d4749ac938af293e4af2de28858858080a1611' | ||||
|                 '2b719514b5e48cb6ce54687e843a4b3e69a04cdb2a9dc99c3b99bdee419fa7d0', | ||||
|                 'b554d182e25fb487a3f2b4285bb8672f98956b5369138e681b467d1f079af116' | ||||
|                 '172d88798345a3a7666faf5f35a144c60812d3234dcd35f444624f2faee16857', | ||||
|                 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce' | ||||
|                 '47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e', | ||||
|                 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce' | ||||
|                 '47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e' | ||||
|                 if connection.features.interprets_empty_strings_as_nulls else None, | ||||
|             ], | ||||
|         ) | ||||
|  | ||||
|     def test_transform(self): | ||||
|         with register_lookup(CharField, SHA512): | ||||
|             authors = Author.objects.filter( | ||||
|                 alias__sha512=( | ||||
|                     'ed014a19bb67a85f9c8b1d81e04a0e7101725be8627d79d02ca4f3bd8' | ||||
|                     '03f33cf3b8fed53e80d2a12c0d0e426824d99d110f0919298a5055eff' | ||||
|                     'f040a3fc091518' | ||||
|                 ), | ||||
|             ).values_list('alias', flat=True) | ||||
|             self.assertSequenceEqual(authors, ['John Smith']) | ||||
		Reference in New Issue
	
	Block a user