diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml index f2c900e5c..6634bf5fc 100644 --- a/.github/workflows/test-python.yml +++ b/.github/workflows/test-python.yml @@ -82,6 +82,7 @@ jobs: expressions.tests.ExpressionsTests.test_insensitive_patterns_escape expressions.tests.ExpressionsTests.test_patterns_escape expressions.tests.FieldTransformTests.test_transform_in_values + expressions.tests.FTimeDeltaTests.test_date_minus_duration expressions.tests.NegatedExpressionTests expressions_case defer diff --git a/README.md b/README.md index 0be92b04e..7fe76a1e7 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,8 @@ Migrations for 'admin': - Queries with joins aren't supported. -- `DateTimeField` doesn't support microsecond precision. +- `DateTimeField` doesn't support microsecond precision, and correspondingly, + `DurationField` stores milliseconds rather than microseconds. - The following database functions aren't supported: - `Chr` diff --git a/django_mongodb/expressions.py b/django_mongodb/expressions.py index fbda1eb99..f08e6c666 100644 --- a/django_mongodb/expressions.py +++ b/django_mongodb/expressions.py @@ -70,6 +70,9 @@ def value(self, compiler, connection): # noqa: ARG001 elif isinstance(value, datetime.date): # Turn dates into datetimes since BSON doesn't support dates. value = datetime.datetime.combine(value, datetime.datetime.min.time()) + elif isinstance(value, datetime.timedelta): + # DurationField stores milliseconds rather than microseconds. + value /= datetime.timedelta(milliseconds=1) return {"$literal": value} diff --git a/django_mongodb/fields/__init__.py b/django_mongodb/fields/__init__.py index eaaee9548..0c5531b89 100644 --- a/django_mongodb/fields/__init__.py +++ b/django_mongodb/fields/__init__.py @@ -1,8 +1,10 @@ from .auto import MongoAutoField +from .duration import register_duration_field from .json import register_json_field __all__ = ["register_fields", "MongoAutoField"] def register_fields(): + register_duration_field() register_json_field() diff --git a/django_mongodb/fields/duration.py b/django_mongodb/fields/duration.py new file mode 100644 index 000000000..cd0fd551d --- /dev/null +++ b/django_mongodb/fields/duration.py @@ -0,0 +1,15 @@ +from django.db.models.fields import DurationField + +_get_db_prep_value = DurationField.get_db_prep_value + + +def get_db_prep_value(self, value, connection, prepared=False): + """DurationField stores milliseconds rather than microseconds.""" + value = _get_db_prep_value(self, value, connection, prepared) + if connection.vendor == "mongodb" and value is not None: + value //= 1000 + return value + + +def register_duration_field(): + DurationField.get_db_prep_value = get_db_prep_value diff --git a/django_mongodb/operations.py b/django_mongodb/operations.py index 87dd92819..ffc53d387 100644 --- a/django_mongodb/operations.py +++ b/django_mongodb/operations.py @@ -101,6 +101,11 @@ def convert_decimalfield_value(self, value, expression, connection): value = value.to_decimal() return value + def convert_durationfield_value(self, value, expression, connection): + if value is not None: + value = datetime.timedelta(milliseconds=value) + return value + def convert_jsonfield_value(self, value, expression, connection): """ Convert dict data to a string so that JSONField.from_db_value() can