Các vấn đề về thời gian của Django (default = datetime.now ())


283

Tôi có mô hình db dưới đây:

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)

Tôi thêm một ví dụ mới bằng cách sử dụng dưới đây:

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

Vấn đề của tôi: tất cả các bản ghi trong cơ sở dữ liệu có cùng giá trị trong trường ngày, đó là ngày thanh toán đầu tiên. Sau khi máy chủ khởi động lại, một bản ghi có ngày mới và các bản ghi khác có cùng bản ghi đầu tiên. Có vẻ như một số dữ liệu được lưu trữ, nhưng tôi không thể tìm thấy ở đâu.

cơ sở dữ liệu: mysql 5.1.25

django v1.1.1


1
Không thể mặc định cho một chức năng như thế này?: default=datetime.now- lưu ý, mà không gọi như trong now()Không phải là tiêu chuẩn cho DateTimeField, nhưng ... tiện dụng bất kỳ.
viridis

Câu trả lời:


597

có vẻ như datetime.now()đang được đánh giá khi mô hình được xác định và không phải mỗi lần bạn thêm một bản ghi.

Django có một tính năng để thực hiện những gì bạn đang cố gắng thực hiện:

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

hoặc là

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

Sự khác biệt giữa ví dụ thứ hai và những gì bạn hiện có là thiếu dấu ngoặc đơn. Bằng cách vượt qua datetime.nowmà không có dấu ngoặc đơn, bạn sẽ truyền chức năng thực tế, sẽ được gọi mỗi khi một bản ghi được thêm vào. Nếu bạn vượt qua nó datetime.now(), thì bạn chỉ cần đánh giá hàm và chuyển cho nó giá trị trả về.

Thông tin thêm có sẵn tại tham chiếu trường mô hình của Django


206
lưu ý quan trọng : sử dụng auto_now_add sẽ hiển thị trường không thể chỉnh sửa trong quản trị viên
Jiaaro

7
Câu trả lời tuyệt vời, cảm ơn. Có cách nào để diễn tả một biểu thức phức tạp hơn với datetime.now làm mặc định không? ví dụ như bây giờ + 30 ngày (sau đây không hoạt động) expiry = models.DateTimeField(default=datetime.now + timedelta(days=30))
michela

26
@michela datetime.now là một hàm và bạn đang cố gắng thêm một timedelta vào một hàm. Bạn sẽ phải xác định cuộc gọi lại của riêng mình để đặt giá trị mặc định, như def now_plus_30(): return datetime.now() + timedelta(days = 30), sau đó sử dụngmodels.DateTimeField(default=now_plus_30)
Carson Myers

55
Hoặc tất nhiên bạn có thể làmdefault=lambda: datetime.now()+timedelta(days=30)
Michael Mior

34
ĐƯỢC BIẾT rằng việc sử dụng auto_now_add là KHÔNG giống như sử dụng một mặc định bởi vì lĩnh vực này sẽ được luôn bằng đến nay (không thể chỉnh sửa) .. Tôi biết rằng điều này là đã nói, nhưng nó không phải chỉ là một Metter của trang 'admin'
VAShhh

114

Thay vì sử dụng, datetime.nowbạn nên thực sự sử dụngfrom django.utils.timezone import now

Tài liệu tham khảo:

vì vậy hãy tìm một cái gì đó như thế này:

from django.utils.timezone import now


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

13
Đây là một lưu ý rất quan trọng! nếu bạn sử dụng datetime.now bạn có thể một datetime cục bộ không nhận biết múi giờ. Nếu sau này bạn so sánh nó với một trường được đánh dấu thời gian bằng cách sử dụng auto_now_add(nhận biết múi giờ), bạn có thể nhận được các phép tính sai do sự khác biệt về múi giờ.
Iftah

1
Điều này chắc chắn rất quan trọng nhưng bản thân nó không thực sự trả lời câu hỏi.
Công nghệ

1
cách dứt khoát tốt hơn
Carmine Tambascia

28

Từ tài liệu về trường mặc định của mô hình django:

Giá trị mặc định cho trường. Đây có thể là một giá trị hoặc một đối tượng có thể gọi được. Nếu có thể gọi được, nó sẽ được gọi mỗi khi một đối tượng mới được tạo.

Do đó, sau đây nên làm việc:

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

1
Điều này chỉ có thể có trong django 1.8+
David Schumann

@DavidNathan tại sao vậy? Tôi nghĩ rằng cả hai tùy chọn đã có từ 1.4 trở về trước, bao gồm cả việc sử dụng một cuộc gọi cho default( django-chinese-docs-14.readthedocs.io/en/latest/ref/models/ trộm )
Đánh dấu

16

David đã có câu trả lời đúng. Dấu ngoặc đơn () làm cho nó sao cho múi giờ có thể gọi được.now () được gọi mỗi khi mô hình được ước tính. Nếu bạn loại bỏ () khỏi timezone.now () (hoặc datetime.now (), nếu sử dụng đối tượng datetime ngây thơ) để làm cho nó chỉ như sau:

default=timezone.now

Sau đó, nó sẽ hoạt động như bạn mong đợi:
Các đối tượng mới sẽ nhận được ngày hiện tại khi chúng được tạo, nhưng ngày sẽ không bị ghi đè mỗi khi bạn thực hiện quản lý makemigations / di chuyển.

Tôi chỉ gặp phải điều này. Rất cám ơn David.


10

Các datetime.now()được đánh giá khi lớp được tạo ra, không phải khi kỷ lục mới đã được bổ sung vào cơ sở dữ liệu.

Để đạt được những gì bạn muốn xác định lĩnh vực này là:

date = models.DateTimeField(auto_now_add=True)

Bằng cách này, datetrường sẽ được đặt thành ngày hiện tại cho mỗi bản ghi mới.


6

Câu trả lời cho điều này là thực sự sai.

Tự động điền vào giá trị (auto_now / auto_now_add không giống như mặc định). Giá trị mặc định sẽ thực sự là những gì người dùng nhìn thấy nếu đó là một đối tượng hoàn toàn mới. Những gì tôi thường làm là:

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

Đảm bảo, nếu bạn cố gắng thể hiện điều này trong trang Quản trị, bạn liệt kê nó là 'read_only' và tham chiếu tên trường

read_only = 'date'

Một lần nữa, tôi làm điều này vì giá trị mặc định của tôi thường không thể chỉnh sửa và các trang Quản trị bỏ qua các mục không thể chỉnh sửa trừ khi được quy định khác. Tuy nhiên, chắc chắn có một sự khác biệt giữa việc đặt giá trị mặc định và triển khai auto_add là chìa khóa ở đây. Kiểm tra nó ra!


6

datetime.now()đang được đánh giá một lần, khi lớp của bạn được khởi tạo. Hãy thử loại bỏ dấu ngoặc để hàm datetime.nowđược trả về và THEN được đánh giá. Tôi gặp vấn đề tương tự với việc đặt giá trị mặc định cho DateTimeFields của tôi và viết lên giải pháp của tôi ở đây .


5

Từ tham chiếu ngôn ngữ Python, dưới định nghĩa Hàm :

Các giá trị tham số mặc định được ước tính khi định nghĩa hàm được thực thi. Điều này có nghĩa là biểu thức được ước tính một lần, khi hàm được xác định và giá trị đó được tính toán trước đó của cùng một giá trị được sử dụng cho mỗi cuộc gọi.

May mắn thay, Django có một cách để làm những gì bạn muốn, nếu bạn sử dụng auto_nowđối số cho DateTimeField:

date = models.DateTimeField(auto_now=True)

Xem tài liệu Django cho DateTimeField .


0

Trong Django 3.0 auto_now_adddường như hoạt động vớiauto_now

reg_date=models.DateField(auto_now=True,blank=True)

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.