ปัญหา Django datetime (ค่าเริ่มต้น = datetime.now ())


283

ฉันมีรูปแบบฐานข้อมูลด้านล่าง:

from datetime import datetime    

class TermPayment(models.Model):
    # I have excluded fields that are irrelevant to the question
    date = models.DateTimeField(default=datetime.now(), blank=True)

ฉันเพิ่มอินสแตนซ์ใหม่โดยใช้ด้านล่าง:

tp = TermPayment.objects.create(**kwargs)

ปัญหาของฉัน: บันทึกทั้งหมดในฐานข้อมูลมีค่าเหมือนกันในฟิลด์วันที่ซึ่งเป็นวันที่ชำระเงินครั้งแรก หลังจากเซิร์ฟเวอร์รีสตาร์ตหนึ่งเรคคอร์ดมีวันที่ใหม่และเรคคอร์ดอื่นมีเหมือนกันกับเรกคอร์ดแรก ดูเหมือนว่าข้อมูลบางส่วนถูกแคช แต่ฉันไม่สามารถหาได้

ฐานข้อมูล: MySQL 5.1.25

django v1.1.1


1
เป็นไปไม่ได้ที่จะตั้งค่าเริ่มต้นเป็นฟังก์ชั่นเช่นนี้หรือไม่: default=datetime.now- หมายเหตุโดยไม่ต้องโทรในในnow()ไม่ใช่มาตรฐานสำหรับ DateTimeField แต่ ... สะดวกทุกอย่าง
viridis

คำตอบ:


597

ดูเหมือนว่าdatetime.now()จะได้รับการประเมินเมื่อมีการกำหนดโมเดลและไม่ใช่ทุกครั้งที่คุณเพิ่มระเบียน

Django มีคุณสมบัติในการบรรลุสิ่งที่คุณพยายามทำอยู่

date = models.DateTimeField(auto_now_add=True, blank=True)

หรือ

date = models.DateTimeField(default=datetime.now, blank=True)

ความแตกต่างระหว่างตัวอย่างที่สองและสิ่งที่คุณมีอยู่ในปัจจุบันคือการขาดวงเล็บ เมื่อผ่านdatetime.nowโดยไม่ใส่วงเล็บคุณจะผ่านฟังก์ชั่นจริงซึ่งจะถูกเรียกทุกครั้งที่มีการเพิ่มเรคคอร์ด ถ้าคุณผ่านมันdatetime.now()ไปคุณก็แค่ประเมินฟังก์ชั่นและส่งมันคืนค่า

ข้อมูลเพิ่มเติมมีอยู่ที่การอ้างอิงเขตข้อมูลโมเดลของ Django


206
หมายเหตุสำคัญ : การใช้ auto_now_add ทำให้ฟิลด์ไม่สามารถแก้ไขได้ในผู้ดูแลระบบ
Jiaaro

7
คำตอบที่ดีขอบคุณ มีวิธีการแสดงออกที่ซับซ้อนมากขึ้นด้วย datetime.now เป็นค่าเริ่มต้นหรือไม่ เช่นตอนนี้ + 30 วัน (รายการต่อไปนี้ใช้ไม่ได้) expiry = models.DateTimeField(default=datetime.now + timedelta(days=30))
michela

26
@michela datetime.now เป็นฟังก์ชันและคุณพยายามเพิ่ม timedelta ให้กับฟังก์ชัน คุณจะต้องกำหนดโทรกลับของคุณเองเพื่อตั้งค่าเริ่มต้นเช่นdef now_plus_30(): return datetime.now() + timedelta(days = 30)แล้วใช้models.DateTimeField(default=now_plus_30)
Carson Myers

55
หรือคุณสามารถทำได้แน่นอนdefault=lambda: datetime.now()+timedelta(days=30)
Michael Mior

34
โปรดทราบว่าการใช้ auto_now_add นั้นไม่เหมือนกับการใช้ค่าเริ่มต้นเนื่องจากฟิลด์จะเท่ากับเสมอ (ไม่มีการแก้ไข) ตอนนี้ .. ฉันรู้ว่านี่ถูกพูดไปแล้ว แต่มันไม่ใช่แค่ตัวชี้วัดของหน้า 'admin'
VAShhh

115

แทนที่จะใช้datetime.nowคุณควรจะใช้จริงๆfrom django.utils.timezone import now

อ้างอิง:

ไปเพื่อสิ่งนี้:

from django.utils.timezone import now


created_date = models.DateTimeField(default=now, editable=False)

13
นี่เป็นบันทึกที่สำคัญมาก! ถ้าคุณใช้ datetime.now คุณสามารถดาต้าไทม์ท้องถิ่นที่ไม่ทราบเขตเวลา หากภายหลังคุณเปรียบเทียบกับเขตข้อมูลที่ใช้การประทับเวลาauto_now_add(ซึ่งตระหนักถึงเขตเวลา) คุณจะได้รับการคำนวณที่ไม่ถูกต้องเนื่องจากความแตกต่างของเขตเวลาที่ผิดพลาด
Iftah

1
สิ่งนี้สำคัญมาก แต่ไม่ได้ตอบคำถามด้วยตัวเอง
Czechnology

1
วิธีที่ดีกว่าอย่างแน่นอน
Carmine Tambascia

28

จากเอกสารในฟิลด์ค่าเริ่มต้นของรุ่น django:

ค่าเริ่มต้นสำหรับฟิลด์ นี่อาจเป็นค่าหรือวัตถุที่เรียกได้ หาก callable มันจะถูกเรียกทุกครั้งที่มีการสร้างวัตถุใหม่

ดังนั้นต่อไปนี้ควรทำงาน:

date = models.DateTimeField(default=datetime.now,blank=True)

1
เป็นไปได้เฉพาะใน django 1.8+
David Schumann

@DavidNathan ทำไมถึงเป็นเช่นนั้น ฉันคิดว่าตัวเลือกทั้งสองมีมาตั้งแต่ 1.4 หรือก่อนหน้ารวมถึงการใช้ callable สำหรับdefault( django-chinese-docs-14.readthedocs.io/en/latest/ref/models/ … )
Mark

16

เดวิดมีคำตอบที่ถูกต้อง เครื่องหมายวงเล็บ () ทำขึ้นเพื่อให้callable timezone.now () ถูกเรียกใช้ทุกครั้งที่โมเดลถูกประเมิน หากคุณลบ () จาก timezone.now () (หรือ datetime.now () หากใช้ออบเจ็กต์ไร้เดียงสา datetime) เพื่อทำสิ่งนี้:

default=timezone.now

จากนั้นจะทำงานตามที่คุณคาดหวัง:
วัตถุใหม่จะได้รับวันที่ปัจจุบันเมื่อสร้าง แต่วันที่จะไม่ถูกแทนที่ทุกครั้งที่คุณจัดการ manage.py makemigrations / โยกย้าย

ฉันเพิ่งพบสิ่งนี้ ขอบคุณมากสำหรับเดวิด


10

datetime.now()จะถูกประเมินเมื่อเรียนจะถูกสร้างขึ้นไม่ได้เมื่อบันทึกใหม่จะถูกเพิ่มลงในฐานข้อมูล

เพื่อให้บรรลุสิ่งที่คุณต้องการกำหนดเขตข้อมูลนี้เป็น:

date = models.DateTimeField(auto_now_add=True)

วิธีdateนี้เขตข้อมูลจะถูกตั้งค่าเป็นวันที่ปัจจุบันสำหรับแต่ละระเบียนใหม่


6

คำตอบสำหรับคนนี้มันผิดจริง

การเติมค่าอัตโนมัติ (auto_now / auto_now_add ไม่เหมือนกับค่าเริ่มต้น) ค่าเริ่มต้นจะเป็นสิ่งที่ผู้ใช้เห็นหากเป็นวัตถุใหม่ สิ่งที่ฉันมักจะทำคือ:

date = models.DateTimeField(default=datetime.now, editable=False,)

ตรวจสอบให้แน่ใจว่าหากคุณพยายามแสดงสิ่งนี้ในหน้าผู้ดูแลระบบแสดงว่าคุณแสดงรายการเป็น 'read_only' และอ้างอิงชื่อฟิลด์

read_only = 'date'

อีกครั้งฉันทำเช่นนี้เนื่องจากค่าเริ่มต้นของฉันไม่สามารถแก้ไขได้โดยทั่วไปและหน้าผู้ดูแลระบบจะเพิกเฉยไม่สามารถแก้ไขได้เว้นแต่จะระบุไว้เป็นอย่างอื่น มีความแตกต่างแน่นอนระหว่างการตั้งค่าเริ่มต้นและการใช้ auto_add ซึ่งเป็นกุญแจสำคัญที่นี่ ลองทดสอบดูสิ!


6

datetime.now()มีการประเมินผลหนึ่งครั้งเมื่อชั้นเรียนของคุณถูกยกตัวอย่าง ลองลบวงเล็บเพื่อให้ฟังก์ชันdatetime.nowส่งคืนและประเมินผลแล้ว ฉันมีปัญหาเดียวกันกับการตั้งค่าเริ่มต้นสำหรับฉันDateTimeFieldและเขียนขึ้นวิธีการแก้ปัญหาของฉันที่นี่


5

จากการอ้างอิงภาษา Python ภายใต้นิยามฟังก์ชั่น :

ค่าพารามิเตอร์เริ่มต้นจะได้รับการประเมินเมื่อมีการดำเนินการกำหนดฟังก์ชัน นี่หมายความว่านิพจน์จะถูกประเมินหนึ่งครั้งเมื่อฟังก์ชันถูกกำหนดและใช้ค่า“ คำนวณล่วงหน้า” เดียวกันสำหรับการโทรแต่ละครั้ง

โชคดีที่ Django มีวิธีการทำสิ่งที่คุณต้องการหากคุณใช้auto_nowอาร์กิวเมนต์สำหรับDateTimeField:

date = models.DateTimeField(auto_now=True)

ดูเอกสาร Django สำหรับDateTimeField


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