Coverage for polar/kit/db/models/base.py: 72%
41 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-12-05 17:15 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-12-05 17:15 +0000
1from datetime import datetime 1ab
2from uuid import UUID 1ab
4from sqlalchemy import TIMESTAMP, MetaData, Uuid, inspect 1ab
5from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column 1ab
7from polar.enums import RateLimitGroup 1ab
8from polar.kit.extensions.sqlalchemy.types import StringEnum 1ab
9from polar.kit.utils import generate_uuid, utc_now 1ab
11my_metadata = MetaData( 1ab
12 naming_convention={
13 "ix": "ix_%(column_0_N_label)s",
14 "uq": "%(table_name)s_%(column_0_N_name)s_key",
15 "ck": "%(table_name)s_%(constraint_name)s_check",
16 "fk": "%(table_name)s_%(column_0_N_name)s_fkey",
17 "pk": "%(table_name)s_pkey",
18 }
19)
22class Model(DeclarativeBase): 1ab
23 __abstract__ = True 1ab
25 metadata = my_metadata 1ab
28class TimestampedModel(Model): 1ab
29 __abstract__ = True 1ab
31 created_at: Mapped[datetime] = mapped_column( 1ab
32 TIMESTAMP(timezone=True), nullable=False, default=utc_now, index=True
33 )
34 modified_at: Mapped[datetime | None] = mapped_column( 1ab
35 TIMESTAMP(timezone=True), onupdate=utc_now, nullable=True, default=None
36 )
37 deleted_at: Mapped[datetime | None] = mapped_column( 1ab
38 TIMESTAMP(timezone=True), nullable=True, default=None, index=True
39 )
41 def set_modified_at(self) -> None: 1ab
42 self.modified_at = utc_now()
44 def set_deleted_at(self) -> None: 1ab
45 self.deleted_at = utc_now()
48class IDModel(Model): 1ab
49 __abstract__ = True 1ab
51 id: Mapped[UUID] = mapped_column(Uuid, primary_key=True, default=generate_uuid) 1ab
53 def __eq__(self, __value: object) -> bool: 1ab
54 return isinstance(__value, self.__class__) and self.id == __value.id
56 def __hash__(self) -> int: 1ab
57 return self.id.int
59 def __repr__(self) -> str: 1ab
60 # We do this complex thing because we might be outside a session with
61 # an expired object; typically when Sentry tries to serialize the object for
62 # error reporting.
63 # But basically, we want to show the ID if we have it.
64 insp = inspect(self)
65 if insp.identity is not None:
66 id_value = insp.identity[0]
67 return f"{self.__class__.__name__}(id={id_value!r})"
68 return f"{self.__class__.__name__}(id=None)"
70 @classmethod 1ab
71 def generate_id(cls) -> UUID: 1ab
72 return generate_uuid()
75class RecordModel(IDModel, TimestampedModel): 1ab
76 __abstract__ = True 1ab
79class RateLimitGroupMixin: 1ab
80 __abstract__ = True 1ab
82 rate_limit_group: Mapped[RateLimitGroup] = mapped_column( 1ab
83 StringEnum(RateLimitGroup, length=16),
84 nullable=False,
85 default=RateLimitGroup.default,
86 )