Refs #28643 -- Added Replace database function.

This commit is contained in:
Mads Jensen 2018-01-18 02:46:15 +01:00 committed by Tim Graham
parent fcd431c6c3
commit 65728550bd
5 changed files with 92 additions and 2 deletions

View File

@ -5,7 +5,9 @@ from .datetime import (
Now, Trunc, TruncDate, TruncDay, TruncHour, TruncMinute, TruncMonth, Now, Trunc, TruncDate, TruncDay, TruncHour, TruncMinute, TruncMonth,
TruncQuarter, TruncSecond, TruncTime, TruncYear, TruncQuarter, TruncSecond, TruncTime, TruncYear,
) )
from .text import Concat, ConcatPair, Length, Lower, StrIndex, Substr, Upper from .text import (
Concat, ConcatPair, Length, Lower, Replace, StrIndex, Substr, Upper,
)
from .window import ( from .window import (
CumeDist, DenseRank, FirstValue, Lag, LastValue, Lead, NthValue, Ntile, CumeDist, DenseRank, FirstValue, Lag, LastValue, Lead, NthValue, Ntile,
PercentRank, Rank, RowNumber, PercentRank, Rank, RowNumber,
@ -21,7 +23,8 @@ __all__ = [
'TruncMinute', 'TruncMonth', 'TruncQuarter', 'TruncSecond', 'TruncTime', 'TruncMinute', 'TruncMonth', 'TruncQuarter', 'TruncSecond', 'TruncTime',
'TruncYear', 'TruncYear',
# text # text
'Concat', 'ConcatPair', 'Length', 'Lower', 'StrIndex', 'Substr', 'Upper', 'Concat', 'ConcatPair', 'Length', 'Lower', 'Replace', 'StrIndex', 'Substr',
'Upper',
# window # window
'CumeDist', 'DenseRank', 'FirstValue', 'Lag', 'LastValue', 'Lead', 'CumeDist', 'DenseRank', 'FirstValue', 'Lag', 'LastValue', 'Lead',
'NthValue', 'Ntile', 'PercentRank', 'Rank', 'RowNumber', 'NthValue', 'Ntile', 'PercentRank', 'Rank', 'RowNumber',

View File

@ -70,6 +70,13 @@ class Lower(Transform):
lookup_name = 'lower' lookup_name = 'lower'
class Replace(Func):
function = 'REPLACE'
def __init__(self, expression, text, replacement=Value(''), **extra):
super().__init__(expression, text, replacement, **extra)
class StrIndex(Func): class StrIndex(Func):
""" """
Return a positive integer corresponding to the 1-indexed position of the Return a positive integer corresponding to the 1-indexed position of the

View File

@ -751,6 +751,28 @@ Usage example::
>>> print(author.name_lower) >>> print(author.name_lower)
margaret smith margaret smith
``Replace``
~~~~~~~~~~~
.. class:: Replace(expression, text, replacement=Value(''), **extra)
.. versionadded:: 2.1
Replaces all occurrences of ``text`` with ``replacement`` in ``expression``.
The default replacement text is the empty string. The arguments to the function
are case-sensitive.
Usage example::
>>> from django.db.models import Value
>>> from django.db.models.functions import Replace
>>> Author.objects.create(name='Margaret Johnson')
>>> Author.objects.create(name='Margaret Smith')
>>> Author.objects.update(name=Replace('name', Value('Margaret'), Value('Margareth')))
2
>>> Author.objects.values('name')
<QuerySet [{'name': 'Margareth Johnson'}, {'name': 'Margareth Smith'}]>
``StrIndex`` ``StrIndex``
------------ ------------

View File

@ -169,6 +169,9 @@ Models
* A ``BinaryField`` may now be set to ``editable=True`` if you wish to include * A ``BinaryField`` may now be set to ``editable=True`` if you wish to include
it in model forms. it in model forms.
* The new :class:`~django.db.models.functions.Replace` database function
replaces strings in an expression.
Requests and Responses Requests and Responses
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,55 @@
from django.db.models import F, Value
from django.db.models.functions import Concat, Replace
from django.test import TestCase
from .models import Author
class ReplaceTests(TestCase):
@classmethod
def setUpTestData(cls):
Author.objects.create(name='George R. R. Martin')
Author.objects.create(name='J. R. R. Tolkien')
def test_replace_with_empty_string(self):
qs = Author.objects.annotate(
without_middlename=Replace(F('name'), Value('R. R. '), Value('')),
)
self.assertQuerysetEqual(qs, [
('George R. R. Martin', 'George Martin'),
('J. R. R. Tolkien', 'J. Tolkien'),
], transform=lambda x: (x.name, x.without_middlename), ordered=False)
def test_case_sensitive(self):
qs = Author.objects.annotate(same_name=Replace(F('name'), Value('r. r.'), Value('')))
self.assertQuerysetEqual(qs, [
('George R. R. Martin', 'George R. R. Martin'),
('J. R. R. Tolkien', 'J. R. R. Tolkien'),
], transform=lambda x: (x.name, x.same_name), ordered=False)
def test_replace_expression(self):
qs = Author.objects.annotate(same_name=Replace(
Concat(Value('Author: '), F('name')), Value('Author: '), Value('')),
)
self.assertQuerysetEqual(qs, [
('George R. R. Martin', 'George R. R. Martin'),
('J. R. R. Tolkien', 'J. R. R. Tolkien'),
], transform=lambda x: (x.name, x.same_name), ordered=False)
def test_update(self):
Author.objects.update(
name=Replace(F('name'), Value('R. R. '), Value('')),
)
self.assertQuerysetEqual(Author.objects.all(), [
('George Martin'),
('J. Tolkien'),
], transform=lambda x: x.name, ordered=False)
def test_replace_with_default_arg(self):
# The default replacement is an empty string.
qs = Author.objects.annotate(same_name=Replace(F('name'), Value('R. R. ')))
self.assertQuerysetEqual(qs, [
('George R. R. Martin', 'George Martin'),
('J. R. R. Tolkien', 'J. Tolkien'),
], transform=lambda x: (x.name, x.same_name), ordered=False)