Skip to content

Commit 6109c57

Browse files
committed
make DurationField store milliseconds instead of microseconds
MongoDB's arithmetic operators like $add treat numbers are milliseconds.
1 parent 5f2a094 commit 6109c57

File tree

6 files changed

+37
-2
lines changed

6 files changed

+37
-2
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ Migrations for 'admin':
118118

119119
- Queries with joins aren't supported.
120120

121-
- `DateTimeField` doesn't support microsecond precision.
121+
- `DateTimeField` doesn't support microsecond precision, and because of that,
122+
`DurationField` stores milliseconds rather than microseconds.
122123

123124
- The following database functions aren't supported:
124125
- `Chr`

django_mongodb/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
check_django_compatability()
88

99
from .expressions import register_expressions # noqa: E402
10+
from .fields import register_fields # noqa: E402
1011
from .functions import register_functions # noqa: E402
1112
from .lookups import register_lookups # noqa: E402
1213
from .query import register_nodes # noqa: E402
1314

1415
register_expressions()
16+
register_fields()
1517
register_functions()
1618
register_lookups()
1719
register_nodes()

django_mongodb/expressions.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ def value(self, compiler, connection): # noqa: ARG001
7070
elif isinstance(value, datetime.date):
7171
# Turn dates into datetimes since BSON doesn't support dates.
7272
value = datetime.datetime.combine(value, datetime.datetime.min.time())
73+
elif isinstance(value, datetime.timedelta):
74+
# DurationField store milliseconds rather than microseconds.
75+
value = value.total_seconds() * 1000
7376
return {"$literal": value}
7477

7578

django_mongodb/fields/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
from .auto import MongoAutoField
2+
from .duration import register_duration_field
23

3-
__all__ = ["MongoAutoField"]
4+
__all__ = ["register_fields", "MongoAutoField"]
5+
6+
7+
def register_fields():
8+
register_duration_field()

django_mongodb/fields/duration.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from decimal import Decimal
2+
3+
from django.db.models.fields import DurationField
4+
5+
_get_db_prep_value = DurationField.get_db_prep_value
6+
7+
8+
def get_db_prep_value(self, value, connection, prepared=False):
9+
"""DurationField stores milliseconds rather than microseconds."""
10+
value = _get_db_prep_value(self, value, connection, prepared)
11+
if connection.vendor == "mongodb" and value is not None:
12+
value = int(Decimal(value) / 1000)
13+
return value
14+
15+
16+
def register_duration_field():
17+
DurationField.get_db_prep_value = get_db_prep_value

django_mongodb/operations.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ def convert_decimalfield_value(self, value, expression, connection):
8989
value = value.to_decimal()
9090
return value
9191

92+
def convert_durationfield_value(self, value, expression, connection):
93+
if value is not None:
94+
# Multiplying value converts milliseconds stored in the database
95+
# to microseconds expected by timedelta.
96+
value = datetime.timedelta(0, 0, value * 1000)
97+
return value
98+
9299
def convert_timefield_value(self, value, expression, connection):
93100
if value is not None:
94101
value = value.time()

0 commit comments

Comments
 (0)