Loading django/db/models/functions.py +33 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,39 @@ Classes that represent database functions. from django.db.models import Func, Transform, Value, fields class Cast(Func): """ Coerce an expression to a new field type. """ function = 'CAST' template = '%(function)s(%(expressions)s AS %(db_type)s)' mysql_types = { fields.CharField: 'char', fields.IntegerField: 'signed integer', fields.FloatField: 'signed', } def __init__(self, expression, output_field): super(Cast, self).__init__(expression, output_field=output_field) def as_sql(self, compiler, connection, **extra_context): if 'db_type' not in extra_context: extra_context['db_type'] = self._output_field.db_type(connection) return super(Cast, self).as_sql(compiler, connection, **extra_context) def as_mysql(self, compiler, connection): extra_context = {} output_field_class = type(self._output_field) if output_field_class in self.mysql_types: extra_context['db_type'] = self.mysql_types[output_field_class] return self.as_sql(compiler, connection, **extra_context) def as_postgresql(self, compiler, connection): # CAST would be valid too, but the :: shortcut syntax is more readable. return self.as_sql(compiler, connection, template='%(expressions)s::%(db_type)s') class Coalesce(Func): """ Chooses, from left to right, the first non-null expression and returns it. Loading docs/ref/models/database-functions.txt +18 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,24 @@ We don't usually recommend allowing ``null=True`` for ``CharField`` since this allows the field to have two "empty values", but it's important for the ``Coalesce`` example below. ``Cast`` ======== .. class:: Cast(expression, output_field) .. versionadded:: 1.10 Forces the result type of ``expression`` to be the one from ``output_field``. Usage example:: >>> from django.db.models import FloatField >>> from django.db.models.functions import Cast >>> Value.objects.create(integer=4) >>> value = Value.objects.annotate(as_float=Cast('integer', FloatField)).get() >>> print(value.as_float) 4.0 ``Coalesce`` ============ Loading docs/releases/1.10.txt +2 −0 Original line number Diff line number Diff line Loading @@ -349,6 +349,8 @@ Models * :meth:`QuerySet.bulk_create() <django.db.models.query.QuerySet.bulk_create>` sets the primary key on objects when using PostgreSQL. * Added the :class:`~django.db.models.functions.Cast` database function. Requests and Responses ~~~~~~~~~~~~~~~~~~~~~~ Loading tests/db_functions/test_cast.py 0 → 100644 +24 −0 Original line number Diff line number Diff line from django.db import models from django.db.models.expressions import Value from django.db.models.functions import Cast from django.test import TestCase from .models import Author class CastTests(TestCase): @classmethod def setUpTestData(self): Author.objects.create(name='Bob', age=1) def test_cast_from_value(self): numbers = Author.objects.annotate(cast_integer=Cast(Value('0'), models.IntegerField())) self.assertEqual(numbers.get().cast_integer, 0) def test_cast_from_field(self): numbers = Author.objects.annotate(cast_string=Cast('age', models.CharField(max_length=255)),) self.assertEqual(numbers.get().cast_string, '1') def test_cast_from_python(self): numbers = Author.objects.annotate(cast_float=Cast(0, models.FloatField())) self.assertEqual(numbers.get().cast_float, 0.0) Loading
django/db/models/functions.py +33 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,39 @@ Classes that represent database functions. from django.db.models import Func, Transform, Value, fields class Cast(Func): """ Coerce an expression to a new field type. """ function = 'CAST' template = '%(function)s(%(expressions)s AS %(db_type)s)' mysql_types = { fields.CharField: 'char', fields.IntegerField: 'signed integer', fields.FloatField: 'signed', } def __init__(self, expression, output_field): super(Cast, self).__init__(expression, output_field=output_field) def as_sql(self, compiler, connection, **extra_context): if 'db_type' not in extra_context: extra_context['db_type'] = self._output_field.db_type(connection) return super(Cast, self).as_sql(compiler, connection, **extra_context) def as_mysql(self, compiler, connection): extra_context = {} output_field_class = type(self._output_field) if output_field_class in self.mysql_types: extra_context['db_type'] = self.mysql_types[output_field_class] return self.as_sql(compiler, connection, **extra_context) def as_postgresql(self, compiler, connection): # CAST would be valid too, but the :: shortcut syntax is more readable. return self.as_sql(compiler, connection, template='%(expressions)s::%(db_type)s') class Coalesce(Func): """ Chooses, from left to right, the first non-null expression and returns it. Loading
docs/ref/models/database-functions.txt +18 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,24 @@ We don't usually recommend allowing ``null=True`` for ``CharField`` since this allows the field to have two "empty values", but it's important for the ``Coalesce`` example below. ``Cast`` ======== .. class:: Cast(expression, output_field) .. versionadded:: 1.10 Forces the result type of ``expression`` to be the one from ``output_field``. Usage example:: >>> from django.db.models import FloatField >>> from django.db.models.functions import Cast >>> Value.objects.create(integer=4) >>> value = Value.objects.annotate(as_float=Cast('integer', FloatField)).get() >>> print(value.as_float) 4.0 ``Coalesce`` ============ Loading
docs/releases/1.10.txt +2 −0 Original line number Diff line number Diff line Loading @@ -349,6 +349,8 @@ Models * :meth:`QuerySet.bulk_create() <django.db.models.query.QuerySet.bulk_create>` sets the primary key on objects when using PostgreSQL. * Added the :class:`~django.db.models.functions.Cast` database function. Requests and Responses ~~~~~~~~~~~~~~~~~~~~~~ Loading
tests/db_functions/test_cast.py 0 → 100644 +24 −0 Original line number Diff line number Diff line from django.db import models from django.db.models.expressions import Value from django.db.models.functions import Cast from django.test import TestCase from .models import Author class CastTests(TestCase): @classmethod def setUpTestData(self): Author.objects.create(name='Bob', age=1) def test_cast_from_value(self): numbers = Author.objects.annotate(cast_integer=Cast(Value('0'), models.IntegerField())) self.assertEqual(numbers.get().cast_integer, 0) def test_cast_from_field(self): numbers = Author.objects.annotate(cast_string=Cast('age', models.CharField(max_length=255)),) self.assertEqual(numbers.get().cast_string, '1') def test_cast_from_python(self): numbers = Author.objects.annotate(cast_float=Cast(0, models.FloatField())) self.assertEqual(numbers.get().cast_float, 0.0)