Refs #28643 -- Added Replace database function.
This commit is contained in:
parent
fcd431c6c3
commit
65728550bd
|
@ -5,7 +5,9 @@ from .datetime import (
|
|||
Now, Trunc, TruncDate, TruncDay, TruncHour, TruncMinute, TruncMonth,
|
||||
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 (
|
||||
CumeDist, DenseRank, FirstValue, Lag, LastValue, Lead, NthValue, Ntile,
|
||||
PercentRank, Rank, RowNumber,
|
||||
|
@ -21,7 +23,8 @@ __all__ = [
|
|||
'TruncMinute', 'TruncMonth', 'TruncQuarter', 'TruncSecond', 'TruncTime',
|
||||
'TruncYear',
|
||||
# text
|
||||
'Concat', 'ConcatPair', 'Length', 'Lower', 'StrIndex', 'Substr', 'Upper',
|
||||
'Concat', 'ConcatPair', 'Length', 'Lower', 'Replace', 'StrIndex', 'Substr',
|
||||
'Upper',
|
||||
# window
|
||||
'CumeDist', 'DenseRank', 'FirstValue', 'Lag', 'LastValue', 'Lead',
|
||||
'NthValue', 'Ntile', 'PercentRank', 'Rank', 'RowNumber',
|
||||
|
|
|
@ -70,6 +70,13 @@ class Lower(Transform):
|
|||
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):
|
||||
"""
|
||||
Return a positive integer corresponding to the 1-indexed position of the
|
||||
|
|
|
@ -751,6 +751,28 @@ Usage example::
|
|||
>>> print(author.name_lower)
|
||||
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``
|
||||
------------
|
||||
|
||||
|
|
|
@ -169,6 +169,9 @@ Models
|
|||
* A ``BinaryField`` may now be set to ``editable=True`` if you wish to include
|
||||
it in model forms.
|
||||
|
||||
* The new :class:`~django.db.models.functions.Replace` database function
|
||||
replaces strings in an expression.
|
||||
|
||||
Requests and Responses
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue