Kaydet (Commit) 4b9d7221 authored tarafından Mads Jensen's avatar Mads Jensen Kaydeden (comit) Tim Graham

Refs #28643 -- Added NullIf database function.

Thanks Nick Pope, Mariusz Felisiak, and Tim Graham for reviews.
üst 217f4456
from .comparison import Cast, Coalesce, Greatest, Least
from .comparison import Cast, Coalesce, Greatest, Least, NullIf
from .datetime import (
Extract, ExtractDay, ExtractHour, ExtractIsoYear, ExtractMinute,
ExtractMonth, ExtractQuarter, ExtractSecond, ExtractWeek, ExtractWeekDay,
......@@ -20,7 +20,7 @@ from .window import (
__all__ = [
# comparison and conversion
'Cast', 'Coalesce', 'Greatest', 'Least',
'Cast', 'Coalesce', 'Greatest', 'Least', 'NullIf',
# datetime
'Extract', 'ExtractDay', 'ExtractHour', 'ExtractMinute', 'ExtractMonth',
'ExtractQuarter', 'ExtractSecond', 'ExtractWeek', 'ExtractWeekDay',
......
"""Database functions that do comparisons or type conversions."""
from django.db.models.expressions import Func
from django.db.models.expressions import Func, Value
class Cast(Func):
......@@ -103,3 +103,14 @@ class Least(Func):
def as_sqlite(self, compiler, connection, **extra_context):
"""Use the MIN function on SQLite."""
return super().as_sqlite(compiler, connection, function='MIN', **extra_context)
class NullIf(Func):
function = 'NULLIF'
arity = 2
def as_oracle(self, compiler, connection, **extra_context):
expression1 = self.get_source_expressions()[0]
if isinstance(expression1, Value) and expression1.value is None:
raise ValueError('Oracle does not allow Value(None) for expression1.')
return super().as_sql(compiler, connection, **extra_context)
......@@ -899,6 +899,8 @@ occur when an Oracle datatype is used as a column name. In
particular, take care to avoid using the names ``date``,
``timestamp``, ``number`` or ``float`` as a field name.
.. _oracle-null-empty-strings:
NULL and empty strings
----------------------
......
......@@ -149,6 +149,25 @@ will result in a database error.
The PostgreSQL behavior can be emulated using ``Coalesce`` if you know
a sensible maximum value to provide as a default.
``NullIf``
----------
.. class:: NullIf(expression1, expression2)
.. versionadded:: 2.2
Accepts two expressions and returns ``None`` if they are equal, otherwise
returns ``expression1``.
.. admonition:: Caveats on Oracle
Due to an :ref:`Oracle convention<oracle-null-empty-strings>`, this
function returns the empty string instead of ``None`` when the expressions
are of type :class:`~django.db.models.CharField`.
Passing ``Value(None)`` to ``expression1`` is prohibited on Oracle since
Oracle doesn't accept ``NULL`` as the first argument.
.. _date-functions:
Date functions
......
......@@ -220,6 +220,9 @@ Models
* Added many :ref:`math database functions <math-functions>`.
* The new :class:`~django.db.models.functions.NullIf` database function
returns ``None`` if the two expressions are equal.
* Setting the new ``ignore_conflicts`` parameter of
:meth:`.QuerySet.bulk_create` to ``True`` tells the database to ignore
failure to insert rows that fail uniqueness constraints or other checks.
......
from unittest import skipUnless
from django.db import connection
from django.db.models import Value
from django.db.models.functions import NullIf
from django.test import TestCase
from ..models import Author
class NullIfTests(TestCase):
@classmethod
def setUpTestData(cls):
Author.objects.create(name='John Smith', alias='smithj')
Author.objects.create(name='Rhonda', alias='Rhonda')
def test_basic(self):
authors = Author.objects.annotate(nullif=NullIf('alias', 'name')).values_list('nullif')
self.assertSequenceEqual(
authors, [
('smithj',),
('' if connection.features.interprets_empty_strings_as_nulls else None,)
]
)
def test_null_argument(self):
authors = Author.objects.annotate(nullif=NullIf('name', Value(None))).values_list('nullif')
self.assertSequenceEqual(authors, [('John Smith',), ('Rhonda',)])
def test_too_few_args(self):
msg = "'NullIf' takes exactly 2 arguments (1 given)"
with self.assertRaisesMessage(TypeError, msg):
NullIf('name')
@skipUnless(connection.vendor == 'oracle', 'Oracle specific test for NULL-literal')
def test_null_literal(self):
msg = 'Oracle does not allow Value(None) for expression1.'
with self.assertRaisesMessage(ValueError, msg):
list(Author.objects.annotate(nullif=NullIf(Value(None), 'name')).values_list('nullif'))
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