Masalah datetime Django (default = datetime.now ())

283

Saya memiliki model db di bawah ini:

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)

Saya menambahkan contoh baru dengan menggunakan di bawah ini:

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

Masalah saya: semua catatan dalam basis data memiliki nilai yang sama di bidang tanggal, yang merupakan tanggal pembayaran pertama. Setelah server dimulai ulang, satu catatan memiliki tanggal baru dan catatan lainnya memiliki yang sama dengan yang pertama. Sepertinya beberapa data di-cache, tetapi saya tidak dapat menemukan di mana.

database: mysql 5.1.25

django v1.1.1

Shamanu4
sumber
1
Apakah tidak mungkin untuk default ke fungsi seperti ini ?: default=datetime.now- note, tanpa memanggil seperti dalam now()Bukan standar untuk DateTimeField, tapi ... berguna anycase.
viridis

Jawaban:

597

sepertinya datetime.now()sedang dievaluasi saat model didefinisikan, dan tidak setiap kali Anda menambahkan catatan.

Django memiliki fitur untuk mencapai apa yang sudah Anda coba lakukan:

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

atau

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

Perbedaan antara contoh kedua dan apa yang saat ini Anda miliki adalah kurangnya tanda kurung. Dengan melewati datetime.nowtanpa tanda kurung, Anda melewati fungsi sebenarnya, yang akan dipanggil setiap kali catatan ditambahkan. Jika Anda melewatinya datetime.now(), maka Anda hanya mengevaluasi fungsi dan meneruskannya nilai pengembalian.

Informasi lebih lanjut tersedia di referensi bidang model Django

Carson Myers
sumber
206
catatan penting : menggunakan auto_now_add membuat bidang tidak dapat diedit di admin
Jiaaro
7
Jawaban yang bagus, terima kasih. Apakah ada cara untuk mengekspresikan ekspresi yang lebih kompleks dengan datetime.now sebagai default? misal sekarang + 30 hari (berikut ini tidak berfungsi) expiry = models.DateTimeField(default=datetime.now + timedelta(days=30))
michela
26
@michela datetime.now adalah fungsi, dan Anda mencoba menambahkan timedelta ke fungsi. Anda harus menentukan callback Anda sendiri untuk menetapkan nilai default, seperti def now_plus_30(): return datetime.now() + timedelta(days = 30), dan kemudian menggunakanmodels.DateTimeField(default=now_plus_30)
Carson Myers
55
Atau tentu saja Anda dapat melakukannyadefault=lambda: datetime.now()+timedelta(days=30)
Michael Mior
34
HATI - HATI bahwa menggunakan auto_now_add TIDAK sama dengan menggunakan default karena bidangnya akan SELALU sama dengan sekarang (tidak dapat diedit) .. Saya tahu ini sudah dikatakan, tetapi ini bukan hanya metter dari halaman 'admin'
VAShhh
115

Alih-alih menggunakan datetime.nowAnda harus benar-benar menggunakanfrom django.utils.timezone import now

Referensi:

jadi lakukan sesuatu seperti ini:

from django.utils.timezone import now


created_date = models.DateTimeField(default=now, editable=False)
andilab
sumber
13
Ini adalah catatan yang sangat penting! jika Anda menggunakan datetime.sekarang Anda bisa datetime lokal yang tidak sadar zona waktu. Jika nanti Anda membandingkannya dengan bidang yang menggunakan timestamp auto_now_add(yang sadar zona waktu), Anda bisa mendapatkan perhitungan yang salah karena ada perbedaan zona waktu.
Iftah
1
Ini jelas sangat penting tetapi tidak benar-benar menjawab pertanyaan itu sendiri.
Czechnology
1
cara yang pasti lebih baik
Carmine Tambascia
28

Dari dokumentasi pada bidang standar model Django:

Nilai default untuk bidang. Ini bisa berupa nilai atau objek yang bisa dipanggil. Jika bisa dipanggil itu akan dipanggil setiap kali objek baru dibuat.

Oleh karena itu berikut ini harus berfungsi:

date = models.DateTimeField(default=datetime.now,blank=True)
mykhal
sumber
1
Ini hanya mungkin di Django 1.8+
David Schumann
@ David Nathan mengapa begitu? Saya pikir kedua opsi telah ada sejak 1.4 atau sebelumnya, termasuk penggunaan callable untuk default( django-chinese-docs-14.readthedocs.io/en/latest/ref/models/… )
Mark
16

David memiliki jawaban yang tepat. Tanda kurung () membuatnya sehingga callable timezone.now () dipanggil setiap kali model dievaluasi. Jika Anda menghapus () dari timezone.now () (atau datetime.now (), jika menggunakan objek datetime yang naif) untuk membuatnya seperti ini:

default=timezone.now

Maka itu akan berfungsi seperti yang Anda harapkan:
Objek baru akan menerima tanggal saat ini ketika mereka dibuat, tetapi tanggal tidak akan ditimpa setiap kali Anda mengelola. Migrasi / migrasi.

Saya baru saja menemukan ini. Terima kasih banyak untuk David.

MicahT
sumber
10

Itu datetime.now()dievaluasi ketika kelas dibuat, bukan ketika catatan baru sedang ditambahkan ke database.

Untuk mencapai apa yang Anda inginkan, tentukan bidang ini sebagai:

date = models.DateTimeField(auto_now_add=True)

Dengan cara ini datebidang akan ditetapkan ke tanggal saat ini untuk setiap catatan baru.

Bartosz
sumber
6

Jawabannya sebenarnya salah.

Mengisi nilai secara otomatis (auto_now / auto_now_add tidak sama dengan default). Nilai default sebenarnya akan menjadi apa yang dilihat pengguna jika itu adalah objek baru. Apa yang biasanya saya lakukan adalah:

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

Pastikan, jika Anda mencoba mewakili ini di halaman Admin, Anda mencantumkannya sebagai 'read_only' dan merujuk nama bidang

read_only = 'date'

Sekali lagi, saya melakukan ini karena nilai default saya biasanya tidak dapat diedit, dan halaman Admin mengabaikan yang tidak dapat diedit kecuali ditentukan lain. Namun ada perbedaan antara pengaturan nilai default dan penerapan auto_add yang merupakan kunci di sini. Uji itu!

Andrew Harris
sumber
6

datetime.now()sedang dievaluasi sekali, ketika kelas Anda dipakai. Coba hapus tanda kurung sehingga fungsi datetime.nowdikembalikan dan MAKA dievaluasi. Saya memiliki masalah yang sama dengan pengaturan nilai default untuk DateTimeFielddan menulis solusi saya di sini .

David
sumber
5

Dari referensi bahasa Python, di bawah Definisi fungsi :

Nilai parameter default dievaluasi ketika definisi fungsi dijalankan. Ini berarti bahwa ekspresi dievaluasi sekali, ketika fungsi didefinisikan, dan bahwa nilai "pra-komputasi" yang sama digunakan untuk setiap panggilan.

Untungnya, Django memiliki cara untuk melakukan apa yang Anda inginkan, jika Anda menggunakan auto_nowargumen untuk DateTimeField:

date = models.DateTimeField(auto_now=True)

Lihat dokumen Django untuk DateTimeField .

ars
sumber
0

Dalam Django 3.0 auto_now_addsepertinya bekerja denganauto_now

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

Bosco Oludhe
sumber