Skip to content

Commit 0aa4ebc

Browse files
authored
Update the model datetime column type to custom (#786)
* Update the model datetime column type to custom * Update the schema datetime filed json encoder
1 parent 30d4107 commit 0aa4ebc

File tree

6 files changed

+43
-25
lines changed

6 files changed

+43
-25
lines changed

backend/app/admin/model/login_log.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
# -*- coding: utf-8 -*-
33
from datetime import datetime
44

5-
from sqlalchemy import DateTime, String
5+
from sqlalchemy import String
66
from sqlalchemy.dialects.mysql import LONGTEXT
77
from sqlalchemy.dialects.postgresql import TEXT
88
from sqlalchemy.orm import Mapped, mapped_column
99

10-
from backend.common.model import DataClassBase, id_key
10+
from backend.common.model import DataClassBase, TimeZone, id_key
1111
from backend.utils.timezone import timezone
1212

1313

@@ -29,7 +29,7 @@ class LoginLog(DataClassBase):
2929
browser: Mapped[str | None] = mapped_column(String(50), comment='浏览器')
3030
device: Mapped[str | None] = mapped_column(String(50), comment='设备')
3131
msg: Mapped[str] = mapped_column(LONGTEXT().with_variant(TEXT, 'postgresql'), comment='提示消息')
32-
login_time: Mapped[datetime] = mapped_column(DateTime(timezone=True), comment='登录时间')
32+
login_time: Mapped[datetime] = mapped_column(TimeZone, comment='登录时间')
3333
created_time: Mapped[datetime] = mapped_column(
34-
DateTime(timezone=True), init=False, default_factory=timezone.now, comment='创建时间'
34+
TimeZone, init=False, default_factory=timezone.now, comment='创建时间'
3535
)

backend/app/admin/model/opera_log.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
# -*- coding: utf-8 -*-
33
from datetime import datetime
44

5-
from sqlalchemy import DateTime, String
5+
from sqlalchemy import String
66
from sqlalchemy.dialects.mysql import JSON, LONGTEXT
77
from sqlalchemy.dialects.postgresql import TEXT
88
from sqlalchemy.orm import Mapped, mapped_column
99

10-
from backend.common.model import DataClassBase, id_key
10+
from backend.common.model import DataClassBase, TimeZone, id_key
1111
from backend.utils.timezone import timezone
1212

1313

@@ -35,7 +35,7 @@ class OperaLog(DataClassBase):
3535
code: Mapped[str] = mapped_column(String(20), insert_default='200', comment='操作状态码')
3636
msg: Mapped[str | None] = mapped_column(LONGTEXT().with_variant(TEXT, 'postgresql'), comment='提示消息')
3737
cost_time: Mapped[float] = mapped_column(insert_default=0.0, comment='请求耗时(ms)')
38-
opera_time: Mapped[datetime] = mapped_column(DateTime(timezone=True), comment='操作时间')
38+
opera_time: Mapped[datetime] = mapped_column(TimeZone, comment='操作时间')
3939
created_time: Mapped[datetime] = mapped_column(
40-
DateTime(timezone=True), init=False, default_factory=timezone.now, comment='创建时间'
40+
TimeZone, init=False, default_factory=timezone.now, comment='创建时间'
4141
)

backend/app/admin/model/user.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
from datetime import datetime
66
from typing import TYPE_CHECKING
77

8-
from sqlalchemy import VARBINARY, Boolean, DateTime, ForeignKey, String
8+
from sqlalchemy import VARBINARY, Boolean, ForeignKey, String
99
from sqlalchemy.dialects.postgresql import BYTEA, INTEGER
1010
from sqlalchemy.orm import Mapped, mapped_column, relationship
1111

1212
from backend.app.admin.model.m2m import sys_user_role
13-
from backend.common.model import Base, id_key
13+
from backend.common.model import Base, TimeZone, id_key
1414
from backend.database.db import uuid4_str
1515
from backend.utils.timezone import timezone
1616

@@ -42,11 +42,9 @@ class User(Base):
4242
is_multi_login: Mapped[bool] = mapped_column(
4343
Boolean().with_variant(INTEGER, 'postgresql'), default=False, comment='是否重复登陆(0否 1是)'
4444
)
45-
join_time: Mapped[datetime] = mapped_column(
46-
DateTime(timezone=True), init=False, default_factory=timezone.now, comment='注册时间'
47-
)
45+
join_time: Mapped[datetime] = mapped_column(TimeZone, init=False, default_factory=timezone.now, comment='注册时间')
4846
last_login_time: Mapped[datetime | None] = mapped_column(
49-
DateTime(timezone=True), init=False, onupdate=timezone.now, comment='上次登录'
47+
TimeZone, init=False, onupdate=timezone.now, comment='上次登录'
5048
)
5149

5250
# 部门用户一对多

backend/app/task/model/scheduler.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from sqlalchemy import (
88
JSON,
99
Boolean,
10-
DateTime,
1110
String,
1211
event,
1312
)
@@ -16,7 +15,7 @@
1615
from sqlalchemy.orm import Mapped, mapped_column
1716

1817
from backend.common.exception import errors
19-
from backend.common.model import Base, id_key
18+
from backend.common.model import Base, TimeZone, id_key
2019
from backend.core.conf import settings
2120
from backend.database.redis import redis_client
2221
from backend.utils.timezone import timezone
@@ -35,8 +34,8 @@ class TaskScheduler(Base):
3534
queue: Mapped[str | None] = mapped_column(String(255), comment='CELERY_TASK_QUEUES 中定义的队列')
3635
exchange: Mapped[str | None] = mapped_column(String(255), comment='低级别 AMQP 路由的交换机')
3736
routing_key: Mapped[str | None] = mapped_column(String(255), comment='低级别 AMQP 路由的路由密钥')
38-
start_time: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), comment='任务开始触发的时间')
39-
expire_time: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), comment='任务不再触发的截止时间')
37+
start_time: Mapped[datetime | None] = mapped_column(TimeZone, comment='任务开始触发的时间')
38+
expire_time: Mapped[datetime | None] = mapped_column(TimeZone, comment='任务不再触发的截止时间')
4039
expire_seconds: Mapped[int | None] = mapped_column(comment='任务不再触发的秒数时间差')
4140
type: Mapped[int] = mapped_column(comment='调度类型(0间隔 1定时)')
4241
interval_every: Mapped[int | None] = mapped_column(comment='任务再次运行前的间隔周期数')
@@ -49,9 +48,7 @@ class TaskScheduler(Base):
4948
Boolean().with_variant(INTEGER, 'postgresql'), default=True, comment='是否启用任务'
5049
)
5150
total_run_count: Mapped[int] = mapped_column(default=0, comment='任务触发的总次数')
52-
last_run_time: Mapped[datetime | None] = mapped_column(
53-
DateTime(timezone=True), default=None, comment='任务最后触发的时间'
54-
)
51+
last_run_time: Mapped[datetime | None] = mapped_column(TimeZone, default=None, comment='任务最后触发的时间')
5552
remark: Mapped[str | None] = mapped_column(
5653
LONGTEXT().with_variant(TEXT, 'postgresql'), default=None, comment='备注'
5754
)

backend/common/model.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from datetime import datetime
44
from typing import Annotated
55

6-
from sqlalchemy import BigInteger, DateTime
6+
from sqlalchemy import BigInteger, DateTime, TypeDecorator
77
from sqlalchemy.ext.asyncio import AsyncAttrs
88
from sqlalchemy.orm import DeclarativeBase, Mapped, MappedAsDataclass, declared_attr, mapped_column
99

@@ -51,14 +51,37 @@ class UserMixin(MappedAsDataclass):
5151
updated_by: Mapped[int | None] = mapped_column(init=False, default=None, sort_order=998, comment='修改者')
5252

5353

54+
class TimeZone(TypeDecorator[datetime]):
55+
"""时区感知 DateTime"""
56+
57+
impl = DateTime(timezone=True)
58+
cache_ok = True
59+
60+
@property
61+
def python_type(self) -> type[datetime]:
62+
return datetime
63+
64+
def process_bind_param(self, value: datetime | None, dialect) -> datetime | None:
65+
if value is not None:
66+
if value.tzinfo != timezone.tz_info:
67+
value = timezone.from_datetime(value)
68+
return value
69+
70+
def process_result_value(self, value: datetime | None, dialect) -> datetime | None:
71+
if value is not None:
72+
if value.tzinfo is None:
73+
value = value.replace(tzinfo=timezone.tz_info)
74+
return value
75+
76+
5477
class DateTimeMixin(MappedAsDataclass):
5578
"""日期时间 Mixin 数据类"""
5679

5780
created_time: Mapped[datetime] = mapped_column(
58-
DateTime(timezone=True), init=False, default_factory=timezone.now, sort_order=999, comment='创建时间'
81+
TimeZone, init=False, default_factory=timezone.now, sort_order=999, comment='创建时间'
5982
)
6083
updated_time: Mapped[datetime | None] = mapped_column(
61-
DateTime(timezone=True), init=False, onupdate=timezone.now, sort_order=999, comment='更新时间'
84+
TimeZone, init=False, onupdate=timezone.now, sort_order=999, comment='更新时间'
6285
)
6386

6487

backend/common/schema.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class SchemaBase(BaseModel):
2525
use_enum_values=True,
2626
json_encoders={
2727
datetime: lambda x: timezone.to_str(timezone.from_datetime(x))
28-
if x.tzinfo is not None
28+
if x.tzinfo is not None and x.tzinfo != timezone.tz_info
2929
else timezone.to_str(x)
3030
},
3131
)

0 commit comments

Comments
 (0)