Ngày giờ mặc định của SQLAlchemy


174

Đây là mô hình khai báo của tôi:

import datetime
from sqlalchemy import Column, Integer, DateTime
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Test(Base):
    __tablename__ = 'test'

    id = Column(Integer, primary_key=True)
    created_date = DateTime(default=datetime.datetime.utcnow)

Tuy nhiên, khi tôi cố gắng nhập mô-đun này, tôi gặp lỗi này:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "orm/models2.py", line 37, in <module>
    class Test(Base):
  File "orm/models2.py", line 41, in Test
    created_date = sqlalchemy.DateTime(default=datetime.datetime.utcnow)
TypeError: __init__() got an unexpected keyword argument 'default'

Nếu tôi sử dụng loại Số nguyên, tôi có thể đặt giá trị mặc định. Chuyện gì đang xảy ra vậy?


1
Điều này không nên được sử dụng. utcnow là dấu thời gian ngây thơ trong múi giờ UTC, tuy nhiên, rất có thể các dấu thời gian ngây thơ được giải thích theo múi giờ địa phương thay thế.
Antti Haapala

1
Tôi biết câu hỏi này đã được hỏi từ lâu nhưng tôi nghĩ câu trả lời của bạn nên được đổi thành câu hỏi mà @Jeff Widman cung cấp vì người kia sẽ sử dụng thời gian "biên dịch" khi lớp bảng được xác định so với khi bản ghi được tạo. Nếu bạn là AFK thì ít nhất bình luận này sẽ đưa ra cảnh báo cho người khác kiểm tra cẩn thận các bình luận cho mỗi câu hỏi.
David

Câu trả lời:


186

DateTimekhông có khóa mặc định làm đầu vào. Khóa mặc định phải là đầu vào cho Columnhàm. Thử cái này:

import datetime
from sqlalchemy import Column, Integer, DateTime
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Test(Base):
    __tablename__ = 'test'

    id = Column(Integer, primary_key=True)
    created_date = Column(DateTime, default=datetime.datetime.utcnow)

13
Điều này không đúng. Dấu thời gian khi tải mô hình sẽ được sử dụng cho tất cả các bản ghi mới thay vì thời gian bản ghi được thêm vào.
SkyLeach

8
@SkyLeach Mã này là chính xác, bạn có thể kiểm tra nó ở đây: pastebin.com/VLyWktUn . datetime.datetime.utcnowdường như được gọi là gọi lại.
bux

1
Tôi đã sử dụng câu trả lời này nhưng dấu thời gian khi tải mô hình đang được sử dụng cho tất cả các bản ghi mới.
scottydelta

105
@scottdelta: Đảm bảo bạn không sử dụng default=datetime.datetime.utcnow(); bạn muốn truyền utcnowchức năng, không phải là kết quả của việc đánh giá nó khi tải mô-đun.
rối loạn chức năng

Vẫn không làm việc cho tôi. Cách tiếp cận của jeff-widman làm việc cho tôi:from sqlalchemy.sql import func; created_date = Column(DateTime(timezone=True), server_default=func.now()
tharndt

396

Tính toán dấu thời gian trong DB của bạn, không phải máy khách của bạn

Đối với sự tỉnh táo, có lẽ bạn muốn tất cả được datetimestính toán bởi máy chủ DB của mình, thay vì máy chủ ứng dụng. Tính toán dấu thời gian trong ứng dụng có thể dẫn đến các vấn đề vì độ trễ của mạng là khác nhau, khách hàng trải nghiệm sự trôi dạt đồng hồ hơi khác nhau và các ngôn ngữ lập trình khác nhau đôi khi tính thời gian hơi khác nhau.

SQLAlchemy cho phép bạn thực hiện điều này bằng cách chuyển func.now()hoặc func.current_timestamp()(chúng là bí danh của nhau) cho DB biết để tự tính toán dấu thời gian.

Sử dụng SQLALchemy server_default

Ngoài ra, đối với một mặc định mà bạn đã yêu cầu DB tính toán giá trị, nói chung nên sử dụng server_defaultthay vì tốt hơn default. Điều này báo cho SQLAlchemy vượt qua giá trị mặc định như một phần của CREATE TABLEcâu lệnh.

Ví dụ: nếu bạn viết một tập lệnh ad hoc dựa vào bảng này, sử dụng server_defaultcó nghĩa là bạn sẽ không cần phải lo lắng về việc thêm thủ công cuộc gọi dấu thời gian vào tập lệnh của mình - cơ sở dữ liệu sẽ tự động đặt nó.

Hiểu về SQLAlchemy onupdate/server_onupdate

SQLAlchemy cũng hỗ trợ onupdateđể bất cứ khi nào hàng được cập nhật, nó sẽ chèn dấu thời gian mới. Một lần nữa, tốt nhất là nói với DB để tự tính toán dấu thời gian:

from sqlalchemy.sql import func

time_created = Column(DateTime(timezone=True), server_default=func.now())
time_updated = Column(DateTime(timezone=True), onupdate=func.now())

Có một server_onupdatetham số, nhưng không giống như server_default, nó không thực sự đặt bất kỳ máy chủ nào. Nó chỉ cho SQLalchemy biết rằng cơ sở dữ liệu của bạn sẽ thay đổi cột khi có cập nhật (có lẽ bạn đã tạo một kích hoạt trên cột ), vì vậy SQLAlchemy sẽ yêu cầu giá trị trả về để nó có thể cập nhật đối tượng tương ứng.

Một Gotcha tiềm năng khác:

Bạn có thể ngạc nhiên khi nhận thấy rằng nếu bạn thực hiện một loạt các thay đổi trong một giao dịch, tất cả chúng đều có cùng dấu thời gian. Đó là bởi vì tiêu chuẩn SQL chỉ định CURRENT_TIMESTAMPtrả về các giá trị dựa trên thời điểm bắt đầu giao dịch.

PostgreSQL cung cấp phi SQL chuẩn statement_timestamp()clock_timestamp()đó làm thay đổi trong một giao dịch. Tài liệu ở đây: https://www.postgresql.org/docs/civerse/static/fifts-datetime.html#FUNCTIONS-DATETIME-CURRENT

Dấu thời gian UTC

Nếu bạn muốn sử dụng dấu thời gian UTC, một sơ khai thực hiện func.utcnow()được cung cấp trong tài liệu SQLAlchemy . Bạn cần phải tự cung cấp các chức năng cụ thể cho trình điều khiển phù hợp.


1
Tôi đã thấy bây giờ có một cách để nói với Postgres sử dụng thời gian hiện tại, không chỉ là thời gian bắt đầu giao dịch ... đó là trong các tài liệu về postgres
Jeff Widman

2
có một func.utcnow () hoặc một cái gì đó như thế không?
yerassyl

1
@JeffWidman: Chúng ta có triển khai mysql cho utcnow không? Tôi trong tài liệu chỉ có mssql và postreg
JavaSa

1
Đây thực sự là một câu trả lời tuyệt vời!
John Mutuma

3
server_default=func.now()làm việc cho tôi, nhưng onupdate=func.now()không. Đã thử onupdate=datetime.datetime.utcnow(không có dấu ngoặc), điều đó cũng không giúp được gì
Vikas Prasad

55

Bạn cũng có thể sử dụng hàm dựng sẵn sqlalchemy DateTime

from sqlalchemy.sql import func

DT = Column(DateTime(timezone=True), default=func.now())

9
Bạn cũng có thể sử dụng server_defaultthay defaultvì giá trị như vậy sẽ được xử lý bởi chính cơ sở dữ liệu.
rgtk

2
Không func.now () được thực thi khi mô tả được xác định, vì vậy tất cả các mô hình / con (nếu đây là lớp cơ sở) sẽ có cùng giá trị DT. Bằng cách chuyển vào một tham chiếu hàm như 'datetime.datetime.utcnow', nó được thực hiện riêng cho từng tham chiếu. Điều này rất quan trọng đối với các thuộc tính 'được tạo' hoặc 'cập nhật'.
Bão kim loại

10
@Mststorm Không, điều này đúng. sqlalchemy.sql.func là trường hợp đặc biệt trả về một thể hiện sqlalchemy.sql.fifts.now, không phải thời điểm hiện tại. docs.sqlalchemy.org/en/latest/core/ Kẻ
Kris Hardy

Không gì timezone=Truelàm gì?
ChaimG

1
@ mình, Từ tài liệu : "Nếu đúng và được hỗ trợ bởi phụ trợ, sẽ tạo ra ' THỜI GIAN VỚI TIMEZONE'. Đối với các phụ trợ không hỗ trợ dấu thời gian nhận biết múi giờ, sẽ không có hiệu lực .
ChaimG

7

Bạn có thể muốn sử dụng onupdate=datetime.nowđể CẬP NHẬT cũng thay đổi last_updatedtrường.

SQLAlchemy có hai mặc định cho các hàm thực thi python.

  • default đặt giá trị trên INSERT, chỉ một lần
  • onupdatecũng đặt giá trị cho kết quả có thể gọi được trên CẬP NHẬT.

Tôi không biết tại sao, nhưng vì một số lý do onupdatekhông làm gì cho tôi.
Vikas Prasad

1
Cuối cùng tôi đã nhận nó làm việc trên Postgres db bằng cách làm này: created_at = db.Column(db.DateTime, server_default=UtcNow())updated_at = db.Column(db.DateTime, server_default=UtcNow(), onupdate=UtcNow())nơi UtcNowlà một lớp học như class UtcNow(expression.FunctionElement): type = DateTime()và chúng tôi có@compiles(UtcNow, 'postgresql') def pg_utc_now(element, compiler, **kw): return "TIMEZONE('utc', CURRENT_TIMESTAMP)"
Vikas Prasad

6

Các defaulttham số từ khóa nên được trao cho các đối tượng Cột.

Thí dụ:

Column(u'timestamp', TIMESTAMP(timezone=True), primary_key=False, nullable=False, default=time_now),

Giá trị mặc định có thể là một cuộc gọi, mà ở đây tôi đã định nghĩa như sau.

from pytz import timezone
from datetime import datetime

UTC = timezone('UTC')

def time_now():
    return datetime.now(UTC)

Nơi nào time_nowđến từ đâu?
kay - SE là ác

Cảm ơn bạn! Tôi giả sử rằng có một hàm tích hợp với tên đó mà bằng cách nào đó tôi đã bỏ qua.
kay - SE là ác

hoặcdatetime.datetime.utcnow
serkef

1

Theo tài liệu PostgreSQL, https://www.postgresql.org/docs/9.6/static/fifts-datetime.html

now, CURRENT_TIMESTAMP, LOCALTIMESTAMP return the time of transaction.

Đây được coi là một tính năng: mục đích là cho phép một giao dịch duy nhất có một khái niệm nhất quán về thời gian "hiện tại", để nhiều sửa đổi trong cùng một giao dịch có cùng dấu thời gian.

Bạn có thể muốn sử dụng statement_timestamp hoặc clock_timestamp nếu bạn không muốn dấu thời gian giao dịch.

statement_timestamp ()

trả về thời gian bắt đầu của câu lệnh hiện tại (cụ thể hơn là thời gian nhận thông báo lệnh mới nhất từ ​​máy khách). statement_timestamp

clock_timestamp ()

trả về thời gian hiện tại thực tế và do đó giá trị của nó thay đổi ngay cả trong một lệnh SQL.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.