diff --git a/django/db/backends/oracle/schema.py b/django/db/backends/oracle/schema.py index 47d60ea290..70698b15e7 100644 --- a/django/db/backends/oracle/schema.py +++ b/django/db/backends/oracle/schema.py @@ -6,6 +6,7 @@ from django.db import DatabaseError from django.db.backends.base.schema import ( BaseDatabaseSchemaEditor, _related_non_m2m_objects, ) +from django.utils.duration import duration_iso_string class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): @@ -27,6 +28,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): def quote_value(self, value): if isinstance(value, (datetime.date, datetime.time, datetime.datetime)): return "'%s'" % value + elif isinstance(value, datetime.timedelta): + return "'%s'" % duration_iso_string(value) elif isinstance(value, str): return "'%s'" % value.replace("\'", "\'\'").replace('%', '%%') elif isinstance(value, (bytes, bytearray, memoryview)): diff --git a/tests/schema/tests.py b/tests/schema/tests.py index 72a73745ed..cd6a52f6af 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -12,10 +12,11 @@ from django.db import ( from django.db.models import ( CASCADE, PROTECT, AutoField, BigAutoField, BigIntegerField, BinaryField, BooleanField, CharField, CheckConstraint, DateField, DateTimeField, - DecimalField, F, FloatField, ForeignKey, ForeignObject, Index, - IntegerField, JSONField, ManyToManyField, Model, OneToOneField, OrderBy, - PositiveIntegerField, Q, SlugField, SmallAutoField, SmallIntegerField, - TextField, TimeField, UniqueConstraint, UUIDField, Value, + DecimalField, DurationField, F, FloatField, ForeignKey, ForeignObject, + Index, IntegerField, JSONField, ManyToManyField, Model, OneToOneField, + OrderBy, PositiveIntegerField, Q, SlugField, SmallAutoField, + SmallIntegerField, TextField, TimeField, UniqueConstraint, UUIDField, + Value, ) from django.db.models.fields.json import KeyTextTransform from django.db.models.functions import Abs, Cast, Collate, Lower, Random, Upper @@ -640,6 +641,19 @@ class SchemaTests(TransactionTestCase): # these two types. self.assertIn(columns['bits'][0], ("BinaryField", "TextField")) + def test_add_field_durationfield_with_default(self): + with connection.schema_editor() as editor: + editor.create_model(Author) + new_field = DurationField(default=datetime.timedelta(minutes=10)) + new_field.set_attributes_from_name('duration') + with connection.schema_editor() as editor: + editor.add_field(Author, new_field) + columns = self.column_classes(Author) + self.assertEqual( + columns['duration'][0], + connection.features.introspected_field_types['DurationField'], + ) + @unittest.skipUnless(connection.vendor == 'mysql', "MySQL specific") def test_add_binaryfield_mediumblob(self): """ @@ -1885,6 +1899,32 @@ class SchemaTests(TransactionTestCase): if not any(details['columns'] == ['height'] and details['check'] for details in constraints.values()): self.fail("No check constraint for height found") + @skipUnlessDBFeature('supports_column_check_constraints', 'can_introspect_check_constraints') + @isolate_apps('schema') + def test_check_constraint_timedelta_param(self): + class DurationModel(Model): + duration = DurationField() + + class Meta: + app_label = 'schema' + + with connection.schema_editor() as editor: + editor.create_model(DurationModel) + self.isolated_local_models = [DurationModel] + constraint_name = 'duration_gte_5_minutes' + constraint = CheckConstraint( + check=Q(duration__gt=datetime.timedelta(minutes=5)), + name=constraint_name, + ) + DurationModel._meta.constraints = [constraint] + with connection.schema_editor() as editor: + editor.add_constraint(DurationModel, constraint) + constraints = self.get_constraints(DurationModel._meta.db_table) + self.assertIn(constraint_name, constraints) + with self.assertRaises(IntegrityError), atomic(): + DurationModel.objects.create(duration=datetime.timedelta(minutes=4)) + DurationModel.objects.create(duration=datetime.timedelta(minutes=10)) + @skipUnlessDBFeature('supports_column_check_constraints', 'can_introspect_check_constraints') def test_remove_field_check_does_not_remove_meta_constraints(self): with connection.schema_editor() as editor: