SQLAlchemy DateTime เริ่มต้น


174

นี่คือรูปแบบการประกาศของฉัน:

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)

อย่างไรก็ตามเมื่อฉันพยายามนำเข้าโมดูลนี้ฉันได้รับข้อผิดพลาดนี้:

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'

หากฉันใช้ประเภทเลขจำนวนเต็มฉันสามารถตั้งค่าเริ่มต้นได้ เกิดอะไรขึ้น?


1
สิ่งนี้ไม่ควรใช้ utcnow เป็น timestamp ที่ไร้เดียงสาในเขตเวลา UTC อย่างไรก็ตามโอกาสที่การตีความ naive timestamps ในเขตเวลาท้องถิ่นจะถูกแทน
Antti Haapala

1
ฉันรู้ว่าคำถามนี้ถูกถามมานานแล้ว แต่ฉันคิดว่าคำตอบของคุณควรเปลี่ยนเป็นคำตอบที่ @Jeff Widman ให้ไว้เพราะอีกอันจะใช้เวลา "รวบรวม" เวลาที่กำหนดคลาสเมื่อเทียบกับเมื่อสร้างตาราง หากคุณเป็น AFK อย่างน้อยความคิดเห็นนี้จะให้คำเตือนแก่ผู้อื่นเพื่อตรวจสอบความคิดเห็นของแต่ละคำถามอย่างรอบคอบ
เดวิด

คำตอบ:


186

DateTimeไม่มีคีย์เริ่มต้นเป็นอินพุต คีย์เริ่มต้นควรเป็นอินพุตของColumnฟังก์ชัน ลองสิ่งนี้:

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
มันไม่ถูกต้อง การประทับเวลาที่โหลดโมเดลจะถูกใช้สำหรับบันทึกใหม่ทั้งหมดแทนที่จะเพิ่มเวลาที่บันทึก
SkyLeach

8
@SkyLeach รหัสนี้ถูกต้องคุณสามารถทดสอบได้ที่นี่: pastebin.com/VLyWktUn datetime.datetime.utcnowดูเหมือนว่าจะโทรกลับ
bux

1
ฉันใช้คำตอบนี้ แต่มีการใช้การประทับเวลาที่ตัวแบบจำลองสำหรับบันทึกใหม่ทั้งหมด
scottydelta

105
@scottdelta: ให้แน่ใจว่าคุณไม่ได้ใช้default=datetime.datetime.utcnow(); คุณต้องการผ่านutcnowฟังก์ชั่นไม่ใช่ผลลัพธ์ของการประเมินที่โหลดโมดูล
ความผิดปกติ

ยังไม่ได้ผลสำหรับฉัน วิธีการของเจฟฟ์-Widman ทำงานสำหรับฉัน:from sqlalchemy.sql import func; created_date = Column(DateTime(timezone=True), server_default=func.now()
tharndt

396

คำนวณเวลาประทับภายในฐานข้อมูลของคุณไม่ใช่ลูกค้าของคุณ

เพื่อความมีสติคุณอาจต้องการdatetimesคำนวณโดยเซิร์ฟเวอร์ DB ของคุณทั้งหมดแทนที่จะเป็นแอปพลิเคชันเซิร์ฟเวอร์ การคำนวณเวลาประทับในแอปพลิเคชันสามารถนำไปสู่ปัญหาได้เนื่องจากเวลาในการตอบสนองของเครือข่ายเป็นตัวแปรไคลเอนต์จะได้สัมผัสกับนาฬิกาที่แตกต่างกันเล็กน้อยและภาษาการเขียนโปรแกรมที่แตกต่างกันในบางครั้ง

SQLAlchemy ช่วยให้คุณทำสิ่งนี้ได้โดยการผ่านfunc.now()หรือfunc.current_timestamp()(เป็นนามแฝงของกันและกัน) ซึ่งบอกให้ DB คำนวณค่าเวลาประทับเอง

ใช้ SQLALchemy's server_default

นอกจากนี้สำหรับเริ่มต้นที่คุณแล้วบอก DB ในการคำนวณค่าโดยทั่วไปดีกว่าที่จะใช้แทนserver_default defaultสิ่งนี้บอกให้ SQLAlchemy ส่งผ่านค่าเริ่มต้นซึ่งเป็นส่วนหนึ่งของCREATE TABLEคำสั่ง

ตัวอย่างเช่นหากคุณเขียนสคริปต์เฉพาะกิจกับตารางนี้การใช้server_defaultหมายความว่าคุณไม่ต้องกังวลเกี่ยวกับการเพิ่มการเรียกเวลาประทับลงในสคริปต์ของคุณด้วยตนเองฐานข้อมูลจะตั้งค่าโดยอัตโนมัติ

ทำความเข้าใจกับ SQLAlchemy onupdate/server_onupdate

SQLAlchemy รองรับonupdateเช่นกันเมื่อใดก็ตามที่แถวนั้นได้รับการปรับปรุงจะมีการประทับเวลาใหม่ อีกครั้งที่ดีที่สุดที่จะบอก DB เพื่อคำนวณการประทับเวลาของตัวเอง:

from sqlalchemy.sql import func

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

มีserver_onupdateพารามิเตอร์ แต่ไม่เหมือนserver_defaultกันที่จริงแล้วมันไม่ได้ตั้งค่าเซิร์ฟเวอร์ใด ๆ เลย มันเพิ่งบอก SQLalchemy ว่าฐานข้อมูลของคุณจะเปลี่ยนคอลัมน์เมื่อมีการอัพเดทเกิดขึ้น (บางทีคุณอาจสร้างทริกเกอร์ในคอลัมน์ ) ดังนั้น SQLAlchemy จะขอค่าส่งคืนเพื่อให้สามารถอัปเดตวัตถุที่สอดคล้องกันได้

gotcha ที่มีศักยภาพอื่น ๆ :

คุณอาจประหลาดใจที่สังเกตเห็นว่าหากคุณทำการเปลี่ยนแปลงหลายอย่างภายในธุรกรรมเดียวพวกเขาทั้งหมดมีเวลาประทับเดียวกัน นั่นเป็นเพราะมาตรฐาน SQL ระบุว่าCURRENT_TIMESTAMPจะส่งคืนค่าตามจุดเริ่มต้นของการทำธุรกรรม

PostgreSQL ให้ไม่ใช่ SQL มาตรฐานstatement_timestamp()และclock_timestamp()ที่ทำการเปลี่ยนแปลงภายในการทำธุรกรรม เอกสารที่นี่: https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT

UTC ประทับเวลา

หากคุณต้องการใช้ UTC timestamps จะมี stub of Implement สำหรับไว้func.utcnow()ในเอกสารคู่มือSQLAlchemy คุณจำเป็นต้องจัดเตรียมฟังก์ชั่นการใช้งานเฉพาะสำหรับไดรเวอร์ให้เหมาะสมด้วยตัวคุณเอง


1
ผมไม่เห็นมีตอนนี้วิธีที่จะบอก Postgres จะใช้เวลาปัจจุบันไม่เพียง แต่เวลาเริ่มต้นการทำธุรกรรม ... มันอยู่ในเอกสาร postgres
เจฟฟ์ Widman

2
มี func.utcnow () หรืออะไรทำนองนั้น
yerassyl

1
@JeffWidman: เรามีการติดตั้ง mysql สำหรับ utcnow หรือไม่? ฉันในเอกสารประกอบเท่านั้น mssql และ postreg
JavaSa

1
นี่เป็นคำตอบที่ดีจริงๆ!
John Mutuma

3
server_default=func.now()ทำงานให้ฉัน แต่onupdate=func.now()ไม่ได้ พยายามonupdate=datetime.datetime.utcnow(ไม่ใส่วงเล็บ) ซึ่งไม่ช่วยด้วย
Vikas Prasad

55

คุณยังสามารถใช้ฟังก์ชัน sqlalchemy builtin เป็นค่าเริ่มต้น DateTime

from sqlalchemy.sql import func

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

9
คุณยังสามารถใช้server_defaultแทนdefaultค่าได้ดังนั้นจะจัดการโดยฐานข้อมูลเอง
rgtk

2
Is func.now () ไม่ทำงานเมื่อกำหนด descriptor ดังนั้นทุกรุ่น / ลูก (ถ้านี่เป็นคลาสพื้นฐาน) จะมีค่า DT เดียวกัน โดยผ่านการอ้างอิงฟังก์ชั่นเช่น 'datetime.datetime.utcnow' มันจะถูกดำเนินการแยกกันสำหรับแต่ละ สิ่งนี้มีความสำคัญสำหรับคุณสมบัติ 'สร้าง' หรือ 'อัพเดต'
Metalstorm

10
@ Metalstorm ไม่ถูกต้อง sqlalchemy.sql.func เป็นกรณีพิเศษที่ส่งกลับอินสแตนซ์ sqlalchemy.sql.functions.now ไม่ใช่เวลาปัจจุบัน docs.sqlalchemy.org/en/latest/core/…
Kris Hardy

อะไรtimezone=Trueทำอย่างไร
ChaimG

1
@ ตนเองจากเอกสาร : "ถ้าเป็น True และรองรับโดยแบ็กเอนด์จะผลิต 'TIMESTAMP พร้อม TIMEZONE' สำหรับแบ็กเอนด์ที่ไม่รองรับการประทับเวลาที่ทราบเขตจะไม่มีผลใด
ChaimG

7

คุณมีแนวโน้มที่จะต้องการใช้onupdate=datetime.nowเพื่อให้ UPDATE เปลี่ยนlast_updatedฟิลด์ด้วย

SQLAlchemy มีสองค่าเริ่มต้นสำหรับฟังก์ชั่นการดำเนินการหลาม

  • default ตั้งค่าเป็น INSERT เพียงครั้งเดียว
  • onupdateตั้งค่าเป็นผลลัพธ์callableใน UPDATE เช่นกัน

ฉันไม่รู้ว่าทำไม แต่ด้วยเหตุผลบางอย่างonupdateไม่ได้ทำอะไรให้ฉันเลย
Vikas Prasad

1
ในที่สุดฉันก็ทำให้มันทำงานกับ Postgres db ได้โดยทำสิ่งนี้created_at = db.Column(db.DateTime, server_default=UtcNow())และชั้นเรียนupdated_at = db.Column(db.DateTime, server_default=UtcNow(), onupdate=UtcNow())ที่ไหนและเรามีUtcNowclass UtcNow(expression.FunctionElement): type = DateTime()@compiles(UtcNow, 'postgresql') def pg_utc_now(element, compiler, **kw): return "TIMEZONE('utc', CURRENT_TIMESTAMP)"
Vikas Prasad

6

defaultพารามิเตอร์คำหลักที่ควรจะได้รับไปยังวัตถุคอลัมน์

ตัวอย่าง:

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

ค่าเริ่มต้นสามารถ callable ซึ่งที่นี่ฉันกำหนดไว้ดังต่อไปนี้

from pytz import timezone
from datetime import datetime

UTC = timezone('UTC')

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

ในกรณีที่ไม่time_nowมาจากไหน?
kay - SE ชั่ว

ขอบคุณ! ฉันคิดว่ามีฟังก์ชั่นในตัวพร้อมชื่อนั้นที่ฉันมองข้ามไป
kay - SE ชั่วร้าย

หรือdatetime.datetime.utcnow
serkef

1

ตามเอกสารของ PostgreSQL ให้https://www.postgresql.org/docs/9.6/static/functions-datetime.html

now, CURRENT_TIMESTAMP, LOCALTIMESTAMP return the time of transaction.

นี่ถือเป็นคุณสมบัติ: เจตนาคือการอนุญาตให้ทำรายการเดียวมีความคิดที่สอดคล้องกันของเวลา "ปัจจุบัน" เพื่อให้การแก้ไขหลายรายการภายในธุรกรรมเดียวกันมีการประทับเวลาเดียวกัน

คุณอาจต้องการใช้statement_timestampหรือclock_timestampถ้าคุณไม่ต้องการ timestamp ธุรกรรม

statement_timestamp ()

ส่งคืนเวลาเริ่มต้นของคำสั่งปัจจุบัน (โดยเฉพาะอย่างยิ่งเวลาที่ได้รับข้อความคำสั่งล่าสุดจากลูกค้า) statement_timestamp

clock_timestamp ()

ส่งคืนเวลาที่เกิดขึ้นจริงในปัจจุบันและดังนั้นจึงมีการเปลี่ยนแปลงค่าแม้ในคำสั่ง SQL เดียว

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.