Unverified Kaydet (Commit) 9ff18c08 authored tarafından Mariusz Felisiak's avatar Mariusz Felisiak Kaydeden (comit) GitHub

Refs #28643 -- Added MD5 database function.

Thanks Tim Graham, Nick Pope and Simon Charette for reviews.
üst 21ff23bf
...@@ -4,6 +4,7 @@ SQLite backend for the sqlite3 module in the standard library. ...@@ -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): ...@@ -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 ( ...@@ -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__ = [ ...@@ -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): ...@@ -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:: ...@@ -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 ...@@ -162,7 +162,7 @@ Migrations
Models Models
~~~~~~ ~~~~~~
* ... * Added the :class:`~django.db.models.functions.MD5` database function.
Requests and Responses Requests and Responses
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
......
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'])
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment