v0.166.0
 1import datetime
 2from collections.abc import Callable
 3from typing import TYPE_CHECKING, Any
 4
 5from plain.utils.dateparse import parse_duration
 6
 7from plain import exceptions
 8
 9from .base import DefaultableField
10
11if TYPE_CHECKING:
12    from plain.postgres.connection import DatabaseConnection
13
14
15class DurationField[
16    T: (datetime.timedelta, datetime.timedelta | None) = datetime.timedelta
17](DefaultableField[T]):
18    """Store timedelta objects using PostgreSQL's interval type."""
19
20    db_type_sql = "interval"
21    empty_strings_allowed = False
22
23    def to_python(self, value: Any) -> datetime.timedelta | None:
24        if value is None:
25            return value
26        if isinstance(value, datetime.timedelta):
27            return value
28        try:
29            parsed = parse_duration(value)
30        except ValueError:
31            pass
32        else:
33            if parsed is not None:
34                return parsed
35
36        raise exceptions.ValidationError(
37            '"%(value)s" value has an invalid format. It must be in [DD] [[HH:]MM:]ss[.uuuuuu] format.',
38            code="invalid",
39            params={"value": value},
40        )
41
42    def get_db_prep_value(
43        self, value: Any, connection: DatabaseConnection, prepared: bool = False
44    ) -> Any:
45        # PostgreSQL has native interval (duration) type
46        return value
47
48    def get_db_converters(
49        self, connection: DatabaseConnection
50    ) -> list[Callable[..., Any]]:
51        # PostgreSQL has native duration field, no converters needed
52        return super().get_db_converters(connection)