Refs #28643 -- Added MD5 database function.
Thanks Tim Graham, Nick Pope and Simon Charette for reviews.
This commit is contained in:
parent
21ff23bfeb
commit
9ff18c08c3
|
@ -4,6 +4,7 @@ SQLite backend for the sqlite3 module in the standard library.
|
||||||
import datetime
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
import functools
|
import functools
|
||||||
|
import hashlib
|
||||||
import math
|
import math
|
||||||
import operator
|
import operator
|
||||||
import re
|
import re
|
||||||
|
@ -217,6 +218,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
conn.create_function('LN', 1, none_guard(math.log))
|
conn.create_function('LN', 1, none_guard(math.log))
|
||||||
conn.create_function('LOG', 2, none_guard(lambda x, y: math.log(y, x)))
|
conn.create_function('LOG', 2, none_guard(lambda x, y: math.log(y, x)))
|
||||||
conn.create_function('LPAD', 3, _sqlite_lpad)
|
conn.create_function('LPAD', 3, _sqlite_lpad)
|
||||||
|
conn.create_function('MD5', 1, none_guard(lambda x: hashlib.md5(x.encode()).hexdigest()))
|
||||||
conn.create_function('MOD', 2, none_guard(math.fmod))
|
conn.create_function('MOD', 2, none_guard(math.fmod))
|
||||||
conn.create_function('PI', 0, lambda: math.pi)
|
conn.create_function('PI', 0, lambda: math.pi)
|
||||||
conn.create_function('POWER', 2, none_guard(operator.pow))
|
conn.create_function('POWER', 2, none_guard(operator.pow))
|
||||||
|
|
|
@ -10,8 +10,9 @@ from .math import (
|
||||||
Mod, Pi, Power, Radians, Round, Sin, Sqrt, Tan,
|
Mod, Pi, Power, Radians, Round, Sin, Sqrt, Tan,
|
||||||
)
|
)
|
||||||
from .text import (
|
from .text import (
|
||||||
Chr, Concat, ConcatPair, Left, Length, Lower, LPad, LTrim, Ord, Repeat,
|
MD5, Chr, Concat, ConcatPair, Left, Length, Lower, LPad, LTrim, Ord,
|
||||||
Replace, Reverse, Right, RPad, RTrim, StrIndex, Substr, Trim, Upper,
|
Repeat, Replace, Reverse, Right, RPad, RTrim, StrIndex, Substr, Trim,
|
||||||
|
Upper,
|
||||||
)
|
)
|
||||||
from .window import (
|
from .window import (
|
||||||
CumeDist, DenseRank, FirstValue, Lag, LastValue, Lead, NthValue, Ntile,
|
CumeDist, DenseRank, FirstValue, Lag, LastValue, Lead, NthValue, Ntile,
|
||||||
|
@ -33,8 +34,8 @@ __all__ = [
|
||||||
'Exp', 'Floor', 'Ln', 'Log', 'Mod', 'Pi', 'Power', 'Radians', 'Round',
|
'Exp', 'Floor', 'Ln', 'Log', 'Mod', 'Pi', 'Power', 'Radians', 'Round',
|
||||||
'Sin', 'Sqrt', 'Tan',
|
'Sin', 'Sqrt', 'Tan',
|
||||||
# text
|
# text
|
||||||
'Chr', 'Concat', 'ConcatPair', 'Left', 'Length', 'Lower', 'LPad', 'LTrim',
|
'MD5', 'Chr', 'Concat', 'ConcatPair', 'Left', 'Length', 'Lower', 'LPad',
|
||||||
'Ord', 'Repeat', 'Replace', 'Reverse', 'Right', 'RPad', 'RTrim',
|
'LTrim', 'Ord', 'Repeat', 'Replace', 'Reverse', 'Right', 'RPad', 'RTrim',
|
||||||
'StrIndex', 'Substr', 'Trim', 'Upper',
|
'StrIndex', 'Substr', 'Trim', 'Upper',
|
||||||
# window
|
# window
|
||||||
'CumeDist', 'DenseRank', 'FirstValue', 'Lag', 'LastValue', 'Lead',
|
'CumeDist', 'DenseRank', 'FirstValue', 'Lag', 'LastValue', 'Lead',
|
||||||
|
|
|
@ -150,6 +150,22 @@ class LTrim(Transform):
|
||||||
lookup_name = 'ltrim'
|
lookup_name = 'ltrim'
|
||||||
|
|
||||||
|
|
||||||
|
class MD5(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):
|
class Ord(Transform):
|
||||||
function = 'ASCII'
|
function = 'ASCII'
|
||||||
lookup_name = 'ord'
|
lookup_name = 'ord'
|
||||||
|
|
|
@ -1303,6 +1303,26 @@ Usage example::
|
||||||
Similar to :class:`~django.db.models.functions.Trim`, but removes only leading
|
Similar to :class:`~django.db.models.functions.Trim`, but removes only leading
|
||||||
spaces.
|
spaces.
|
||||||
|
|
||||||
|
``MD5``
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. class:: MD5(expression, **extra)
|
||||||
|
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
|
||||||
|
Accepts a single text field or expression and returns the MD5 hash of the
|
||||||
|
string.
|
||||||
|
|
||||||
|
It can also be registered as a transform as described in :class:`Length`.
|
||||||
|
|
||||||
|
Usage example::
|
||||||
|
|
||||||
|
>>> from django.db.models.functions import MD5
|
||||||
|
>>> Author.objects.create(name='Margaret Smith')
|
||||||
|
>>> author = Author.objects.annotate(name_md5=MD5('name')).get()
|
||||||
|
>>> print(author.name_md5)
|
||||||
|
749fb689816b2db85f5b169c2055b247
|
||||||
|
|
||||||
``Ord``
|
``Ord``
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,7 @@ Migrations
|
||||||
Models
|
Models
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
|
||||||
* ...
|
* Added the :class:`~django.db.models.functions.MD5` database function.
|
||||||
|
|
||||||
Requests and Responses
|
Requests and Responses
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
from django.db import connection
|
||||||
|
from django.db.models import CharField
|
||||||
|
from django.db.models.functions import MD5
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.test.utils import register_lookup
|
||||||
|
|
||||||
|
from ..models import Author
|
||||||
|
|
||||||
|
|
||||||
|
class MD5Tests(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(
|
||||||
|
md5_alias=MD5('alias'),
|
||||||
|
).values_list('md5_alias', flat=True).order_by('pk')
|
||||||
|
self.assertSequenceEqual(
|
||||||
|
authors,
|
||||||
|
[
|
||||||
|
'6117323d2cabbc17d44c2b44587f682c',
|
||||||
|
'ca6d48f6772000141e66591aee49d56c',
|
||||||
|
'bf2c13bc1154e3d2e7df848cbc8be73d',
|
||||||
|
'd41d8cd98f00b204e9800998ecf8427e',
|
||||||
|
'd41d8cd98f00b204e9800998ecf8427e' if connection.features.interprets_empty_strings_as_nulls else None,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_transform(self):
|
||||||
|
with register_lookup(CharField, MD5):
|
||||||
|
authors = Author.objects.filter(
|
||||||
|
alias__md5='6117323d2cabbc17d44c2b44587f682c',
|
||||||
|
).values_list('alias', flat=True)
|
||||||
|
self.assertSequenceEqual(authors, ['John Smith'])
|
Loading…
Reference in New Issue