Refs #28643 -- Added MD5 database function.

Thanks Tim Graham, Nick Pope and Simon Charette for reviews.
This commit is contained in:
Mariusz Felisiak 2019-02-21 10:52:51 +01:00 committed by GitHub
parent 21ff23bfeb
commit 9ff18c08c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 85 additions and 5 deletions

View File

@ -4,6 +4,7 @@ SQLite backend for the sqlite3 module in the standard library.
import datetime
import decimal
import functools
import hashlib
import math
import operator
import re
@ -217,6 +218,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
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('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('PI', 0, lambda: math.pi)
conn.create_function('POWER', 2, none_guard(operator.pow))

View File

@ -10,8 +10,9 @@ from .math import (
Mod, Pi, Power, Radians, Round, Sin, Sqrt, Tan,
)
from .text import (
Chr, Concat, ConcatPair, Left, Length, Lower, LPad, LTrim, Ord, Repeat,
Replace, Reverse, Right, RPad, RTrim, StrIndex, Substr, Trim, Upper,
MD5, 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,
@ -33,8 +34,8 @@ __all__ = [
'Exp', 'Floor', 'Ln', 'Log', 'Mod', 'Pi', 'Power', 'Radians', 'Round',
'Sin', 'Sqrt', 'Tan',
# text
'Chr', 'Concat', 'ConcatPair', 'Left', 'Length', 'Lower', 'LPad', 'LTrim',
'Ord', 'Repeat', 'Replace', 'Reverse', 'Right', 'RPad', 'RTrim',
'MD5', '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',

View File

@ -150,6 +150,22 @@ class LTrim(Transform):
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):
function = 'ASCII'
lookup_name = 'ord'

View File

@ -1303,6 +1303,26 @@ Usage example::
Similar to :class:`~django.db.models.functions.Trim`, but removes only leading
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``
-------

View File

@ -162,7 +162,7 @@ Migrations
Models
~~~~~~
* ...
* Added the :class:`~django.db.models.functions.MD5` database function.
Requests and Responses
~~~~~~~~~~~~~~~~~~~~~~

View File

@ -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'])