1from __future__ import annotations
2
3from collections.abc import Callable, Sequence
4from functools import cached_property
5from typing import TYPE_CHECKING, Any
6from uuid import UUID
7
8from plain import exceptions
9
10from .base import ColumnField
11
12if TYPE_CHECKING:
13 from plain.postgres.connection import DatabaseConnection
14 from plain.postgres.expressions import Func
15
16
17class UUIDField(ColumnField[UUID]):
18 db_type_sql = "uuid"
19 empty_strings_allowed = False
20
21 def __init__(
22 self,
23 *,
24 generate: bool = False,
25 required: bool = True,
26 allow_null: bool = False,
27 validators: Sequence[Callable[..., Any]] = (),
28 ):
29 self.generate = generate
30 super().__init__(
31 required=required,
32 allow_null=allow_null,
33 validators=validators,
34 )
35
36 @cached_property
37 def _db_default_expression(self) -> Func | None:
38 if self.generate:
39 from plain.postgres.functions.uuid import GenRandomUUID
40
41 return GenRandomUUID()
42 return None
43
44 def get_db_default_expression(self) -> Func | None:
45 return self._db_default_expression
46
47 def deconstruct(self) -> tuple[str | None, str, list[Any], dict[str, Any]]:
48 name, path, args, kwargs = super().deconstruct()
49 if self.generate:
50 kwargs["generate"] = True
51 return name, path, args, kwargs
52
53 def get_prep_value(self, value: Any) -> Any:
54 value = super().get_prep_value(value)
55 return self.to_python(value)
56
57 def get_db_prep_value(
58 self, value: Any, connection: DatabaseConnection, prepared: bool = False
59 ) -> UUID | None:
60 # PostgreSQL has native UUID type
61 if value is None:
62 return None
63 if not isinstance(value, UUID):
64 value = self.to_python(value)
65 return value
66
67 def to_python(self, value: Any) -> UUID | None:
68 if value is not None and not isinstance(value, UUID):
69 input_form = "int" if isinstance(value, int) else "hex"
70 try:
71 return UUID(**{input_form: value})
72 except (AttributeError, ValueError):
73 raise exceptions.ValidationError(
74 '"%(value)s" is not a valid UUID.',
75 code="invalid",
76 params={"value": value},
77 )
78 return value