Skip to content

Commit ecf206d

Browse files
authored
Merge pull request #82 from igorbenav/79-datetimeutcnow-is-deprecated-as-of-python-312
79 datetimeutcnow is deprecated as of python 312
2 parents a9eca16 + 11786ad commit ecf206d

File tree

11 files changed

+33
-32
lines changed

11 files changed

+33
-32
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,7 @@ crud_users.update(db=db, object={name="Updated Name"}, username="myusername")
734734
To delete we have two options:
735735
- db_delete: actually deletes the row from the database
736736
- delete:
737-
- adds `"is_deleted": True` and `deleted_at: datetime.utcnow()` if the model inherits from `PersistentDeletion` (performs a soft delete), but keeps the object in the database.
737+
- adds `"is_deleted": True` and `deleted_at: datetime.now(UTC)` if the model inherits from `PersistentDeletion` (performs a soft delete), but keeps the object in the database.
738738
- actually deletes the row from the database if the model does not inherit from `PersistentDeletion`
739739

740740
```python

src/app/core/db/models.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import uuid as uuid_pkg
2-
from datetime import datetime
2+
from datetime import datetime, UTC
33
from sqlalchemy import Column, DateTime, Boolean, text
44
from sqlalchemy.dialects.postgresql import UUID
55

@@ -8,8 +8,8 @@ class UUIDMixin:
88

99

1010
class TimestampMixin:
11-
created_at: datetime = Column(DateTime, default=datetime.utcnow, server_default=text("current_timestamp(0)"))
12-
updated_at: datetime = Column(DateTime, nullable=True, onupdate=datetime.utcnow, server_default=text("current_timestamp(0)"))
11+
created_at: datetime = Column(DateTime, default=datetime.now(UTC), server_default=text("current_timestamp(0)"))
12+
updated_at: datetime = Column(DateTime, nullable=True, onupdate=datetime.now(UTC), server_default=text("current_timestamp(0)"))
1313

1414

1515
class SoftDeleteMixin:

src/app/core/schemas.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Any
22
import uuid as uuid_pkg
3-
from datetime import datetime
3+
from datetime import datetime, UTC
44

55
from pydantic import BaseModel, Field, field_serializer
66

@@ -15,7 +15,7 @@ class UUIDSchema(BaseModel):
1515

1616

1717
class TimestampSchema(BaseModel):
18-
created_at: datetime = Field(default_factory=datetime.utcnow)
18+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC).replace(tzinfo=None))
1919
updated_at: datetime = Field(default=None)
2020

2121
@field_serializer("created_at")

src/app/core/security.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from typing import Union, Literal, Dict, Any
2-
from datetime import datetime, timedelta
2+
from datetime import datetime, timedelta, UTC
33

44
import bcrypt
55
from sqlalchemy.ext.asyncio import AsyncSession
@@ -43,19 +43,19 @@ async def authenticate_user(username_or_email: str, password: str, db: AsyncSess
4343
async def create_access_token(data: dict[str, Any], expires_delta: timedelta | None = None) -> str:
4444
to_encode = data.copy()
4545
if expires_delta:
46-
expire = datetime.utcnow() + expires_delta
46+
expire = datetime.now(UTC).replace(tzinfo=None) + expires_delta
4747
else:
48-
expire = datetime.utcnow() + timedelta(minutes=15)
48+
expire = datetime.now(UTC).replace(tzinfo=None) + timedelta(minutes=15)
4949
to_encode.update({"exp": expire})
5050
encoded_jwt: str = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
5151
return encoded_jwt
5252

5353
async def create_refresh_token(data: dict[str, Any], expires_delta: timedelta | None = None) -> str:
5454
to_encode = data.copy()
5555
if expires_delta:
56-
expire = datetime.utcnow() + expires_delta
56+
expire = datetime.now(UTC).replace(tzinfo=None) + expires_delta
5757
else:
58-
expire = datetime.utcnow() + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
58+
expire = datetime.now(UTC).replace(tzinfo=None) + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
5959
to_encode.update({"exp": expire})
6060
encoded_jwt: str = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
6161
return encoded_jwt

src/app/core/utils/rate_limit.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from datetime import datetime
1+
from datetime import datetime, UTC
22

33
from redis.asyncio import Redis, ConnectionPool
44
from sqlalchemy.ext.asyncio import AsyncSession
@@ -22,7 +22,7 @@ async def is_rate_limited(
2222
logger.error("Redis client is not initialized.")
2323
raise Exception("Redis client is not initialized.")
2424

25-
current_timestamp = int(datetime.utcnow().timestamp())
25+
current_timestamp = int(datetime.now(UTC).timestamp())
2626
window_start = current_timestamp - (current_timestamp % period)
2727

2828
sanitized_path = sanitize_path(path)

src/app/crud/crud_base.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from typing import Any, Dict, Generic, List, Type, TypeVar, Union
2-
from datetime import datetime
2+
from datetime import datetime, UTC
33

44
from pydantic import BaseModel
55
from sqlalchemy import select, update, delete, func, and_, inspect
@@ -444,7 +444,7 @@ async def update(
444444
update_data = object.model_dump(exclude_unset=True)
445445

446446
if "updated_at" in update_data.keys():
447-
update_data["updated_at"] = datetime.utcnow()
447+
update_data["updated_at"] = datetime.now(UTC)
448448

449449
stmt = update(self._model) \
450450
.filter_by(**kwargs) \
@@ -503,7 +503,7 @@ async def delete(
503503
if "is_deleted" in self._model.__table__.columns:
504504
object_dict = {
505505
"is_deleted": True,
506-
"deleted_at": datetime.utcnow()
506+
"deleted_at": datetime.now(UTC)
507507
}
508508
stmt = update(self._model) \
509509
.filter_by(**kwargs) \

src/app/models/post.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
from typing import Optional
12
import uuid as uuid_pkg
2-
from datetime import datetime
3+
from datetime import datetime, UTC
34

45
from sqlalchemy import String, DateTime, ForeignKey
56
from sqlalchemy.orm import Mapped, mapped_column
@@ -21,8 +22,8 @@ class Post(Base):
2122
media_url: Mapped[str | None] = mapped_column(String, default=None)
2223

2324
created_at: Mapped[datetime] = mapped_column(
24-
DateTime, default_factory=datetime.utcnow
25+
DateTime(timezone=True), default_factory=lambda: datetime.now(UTC)
2526
)
26-
updated_at: Mapped[datetime | None] = mapped_column(default=None)
27-
deleted_at: Mapped[datetime | None] = mapped_column(default=None)
27+
updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), default=None)
28+
deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), default=None)
2829
is_deleted: Mapped[bool] = mapped_column(default=False, index=True)

src/app/models/rate_limit.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from typing import Optional
2-
from datetime import datetime
2+
from datetime import datetime, UTC
33

44
from sqlalchemy import String, DateTime, ForeignKey, Integer
55
from sqlalchemy.orm import Mapped, mapped_column
@@ -19,6 +19,6 @@ class RateLimit(Base):
1919
period: Mapped[int] = mapped_column(Integer, nullable=False)
2020

2121
created_at: Mapped[datetime] = mapped_column(
22-
DateTime, default_factory=datetime.utcnow
22+
DateTime(timezone=True), default_factory=lambda: datetime.now(UTC)
2323
)
24-
updated_at: Mapped[Optional[datetime]] = mapped_column(default=None)
24+
updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), default=None)

src/app/models/tier.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from typing import Optional
2-
from datetime import datetime
2+
from datetime import datetime, UTC
33

44
from sqlalchemy import String, DateTime
55
from sqlalchemy.orm import Mapped, mapped_column
@@ -15,6 +15,6 @@ class Tier(Base):
1515
name: Mapped[str] = mapped_column(String, nullable=False, unique=True)
1616

1717
created_at: Mapped[datetime] = mapped_column(
18-
DateTime, default_factory=datetime.utcnow
18+
DateTime(timezone=True), default_factory=lambda: datetime.now(UTC)
1919
)
20-
updated_at: Mapped[Optional[datetime]] = mapped_column(default=None)
20+
updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), default=None)

src/app/models/user.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Optional
22
import uuid as uuid_pkg
3-
from datetime import datetime
3+
from datetime import datetime, UTC
44

55
from sqlalchemy import String, DateTime, ForeignKey
66
from sqlalchemy.orm import Mapped, mapped_column
@@ -24,10 +24,10 @@ class User(Base):
2424
default_factory=uuid_pkg.uuid4, primary_key=True, unique=True
2525
)
2626
created_at: Mapped[datetime] = mapped_column(
27-
DateTime, default_factory=datetime.utcnow
27+
DateTime(timezone=True), default_factory=lambda: datetime.now(UTC)
2828
)
29-
updated_at: Mapped[Optional[datetime]] = mapped_column(default=None)
30-
deleted_at: Mapped[Optional[datetime]] = mapped_column(default=None)
29+
updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), default=None)
30+
deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), default=None)
3131
is_deleted: Mapped[bool] = mapped_column(default=False, index=True)
3232
is_superuser: Mapped[bool] = mapped_column(default=False)
3333

src/scripts/create_first_superuser.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
)
1414
from sqlalchemy.dialects.postgresql import UUID
1515
import uuid
16-
from datetime import datetime
16+
from datetime import datetime, UTC
1717

1818
from ..app.core.db.database import async_engine
1919
from ..app.core.db.database import AsyncSession, local_session
@@ -42,7 +42,7 @@ async def create_first_user(session: AsyncSession) -> None:
4242
Column("hashed_password", String, nullable=False),
4343
Column("profile_image_url", String, default="https://profileimageurl.com"),
4444
Column("uuid", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True),
45-
Column("created_at", DateTime, default=datetime.utcnow, nullable=False),
45+
Column("created_at", DateTime(timezone=True), default=lambda: datetime.now(UTC), nullable=False),
4646
Column("updated_at", DateTime),
4747
Column("deleted_at", DateTime),
4848
Column("is_deleted", Boolean, default=False, index=True),

0 commit comments

Comments
 (0)