Django auto_now ve auto_now_add


272

Django 1.1 için.

Benim models.py bu var:

class User(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

Bir satırı güncellerken şunu elde ederim:

[Sun Nov 15 02:18:12 2009] [error] /home/ptarjan/projects/twitter-meme/django/db/backends/mysql/base.py:84: Warning: Column 'created' cannot be null
[Sun Nov 15 02:18:12 2009] [error]   return self.cursor.execute(query, args)

Veritabanımın ilgili kısmı:

  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,

Bu endişe kaynağı mı?

Yan soru: Yönetici aracımda bu iki alan görünmüyor. Bu beklenen mi?


3
varsayılan otomatik artış int yerine özel bir birincil anahtar mı kullanıyordunuz? Özel bir birincil anahtar kullanmanın bu soruna neden olduğunu keşfettim. Her neyse, sanırım şimdiye kadar çözdün. Ancak hata hala var. Sadece benim 0.02 $
tapan

3
Hatırlatmak için bir şey daha var. update()yöntemi, alanı otomatik olarak save()güncelleyemediği anlamına gelmezmodified
Chemical Programmer

Yanıtlar:


383

auto_nowÖznitelik ayarlanmış herhangi bir alan da devralınır editable=Falseve bu nedenle yönetici panelinde görünmez. Geçmişte auto_nowve auto_now_addargümanlarının ortadan kaldırılması hakkında konuşulmuştu ve hala var olmalarına rağmen, sadece özel bir save()yöntem kullanarak daha iyi olduğunuzu hissediyorum .

Bu nedenle, düzgün çalışmasını sağlamak için , yalnızca ayarlanmadıysa (öğenin ilk oluşturulduğu zaman gibi) güncellenmesini sağlamak için kendi yönteminizi kullanmamayı auto_nowveya auto_now_addbunun yerine tanımlamanızı ve öğenin her seferinde güncellenmesini öneririm kaydedilir.save()createdidmodified

Django kullanarak yazdığım diğer projelerle aynı şeyi yaptım ve böylece save()şöyle görüneceksin:

from django.utils import timezone

class User(models.Model):
    created     = models.DateTimeField(editable=False)
    modified    = models.DateTimeField()

    def save(self, *args, **kwargs):
        ''' On save, update timestamps '''
        if not self.id:
            self.created = timezone.now()
        self.modified = timezone.now()
        return super(User, self).save(*args, **kwargs)

Bu yardımcı olur umarım!

Yorumlara yanıt olarak düzenleyin:

save()Bu alan argümanlarına güvenmek yerine aşırı yüklenmeye bağlı kalmamın nedeni iki katlıdır:

  1. Söz konusu iniş çıkışlar güvenilirlikleri ile birlikte. Bu argümanlar, Django'nun nasıl etkileşime gireceğini bildiği her bir veritabanı türünün bir tarih / zaman damgası alanını tedavi ettiği ve her sürüm arasında kırıldığı ve / veya değiştiği görülüyor. (Ki bunların tamamen kaldırılması çağrısının ardındaki itici güç olduğuna inanıyorum).
  2. Yalnızca DateField, DateTimeField ve TimeField üzerinde çalışıyor olmaları ve bu tekniği kullanarak her öğe kaydedildiğinde herhangi bir alan türünü otomatik olarak doldurabilirsiniz.
  3. Kullanım django.utils.timezone.now()vs datetime.datetime.now(), çünkü buna datetime.datetimebağlı olarak TZ uyumlu veya saf bir nesne döndürür settings.USE_TZ.

OP'nin hatayı neden gördüğünü ele almak için tam olarak bilmiyorum, ancak createdsahip olmasına rağmen hiç doldurulmamış gibi görünüyor auto_now_add=True. Bana göre bir hata olarak öne çıkıyor ve yukarıdaki küçük listemde # 1'in altını çiziyor: auto_nowve auto_now_adden iyi ihtimalle lapa lapa.


9
Ancak yazarın sorunun kaynağı nedir? Auto_now_add bazen yanlış çalışıyor mu?
Dmitry Risenberg

5
Ben seninleyim Dmitry. Neden iki alanın hata attığını merak ediyorum .. Ve neden kendi özel save () yöntemini yazmanın daha iyi olduğunu düşündüğünüzü merak ediyorum.
hora

45
save()Modellerimin her birine özel bir yazmak , auto_now(bu alanları tüm modellerimde bulundurmayı sevdiğim için) kullanmaktan çok daha acı verici . Bu parametreler neden çalışmıyor?
Paul Tarjan

3
@TM, ancak Django şemayı tanımlamak için sadece models.py dosyalarını hedeflerken doğrudan db'nizle uğraşmayı gerektirir
akaihola

11
Kesinlikle katılmıyorum. 1) editable = False doğrudur, alanı düzenlememelisiniz, veritabanınızın doğru olması gerekir. 2) Özel SQL güncellemeleri veya ne kullanılırsa kullanılsın, save () işlevinin çağrılmayabileceği her tür kenar durumu vardır. 3) Bu, veritabanlarının aslında referans bütünlüğü ile birlikte iyi olduğu bir şeydir. Veritabanının doğru olması için güvenmek iyi bir varsayılan, çünkü sizden ya da benden daha akıllı beyinler bu şekilde çalışmak için veritabanını tasarladık.
Shayne

175

Ancak kabul edilen cevapta ifade edilen görüşün biraz modası geçmiş olduğunu belirtmek istedim . Daha yeni tartışmalara (django hataları # 7634 ve # 12785 ) göre, auto_now ve auto_now_add hiçbir yere gitmiyor ve orijinal tartışmaya gitseniz bile , özel tasarrufta RY'ye (DRY'de olduğu gibi) karşı güçlü argümanlar bulacaksınız yöntemleri.

Daha iyi bir çözüm önerildi (özel alan türleri), ancak django'ya dönüştürmek için yeterli ivme kazanmadı. Kendinizi üç satıra yazabilirsiniz ( Jacob Kaplan-Moss'un önerisi ).

from django.db import models
from django.utils import timezone


class AutoDateTimeField(models.DateTimeField):
    def pre_save(self, model_instance, add):
        return timezone.now()

#usage
created_at = models.DateField(default=timezone.now)
updated_at = models.AutoDateTimeField(default=timezone.now)

1
Üç satırlı özel alan burada: bağlantı
hgcrpd

Varsayılan olarak bir çağrılabilir (yani, timezone.now) ayarlayabilirsiniz göz önüne alındığında özel bir alan gerçekten gerekli olduğunu sanmıyorum. Cevabımı aşağıda görebilirsiniz.
Josh

6
Bu auto_add'nin Django'da yaptığı şeyle aynıdır ve 2010'dan beri sahip olduğu: github.com/django/django/blob/1.8.4/django/db/models/fields/… . Ben pre_save ek kanca gerek sürece, ben auto_add ile yapışıyorum.
jwhitlock

1
Django 1.9 ile benim için çalışmadı, bu yüzden bu çözüm her zaman işe yaramaz, çünkü auto_now * için asla olmadı. Her kullanım durumunda çalışan tek çözüm ('update_fields' arg probleminde bile) kaydetmeyi geçersiz
kılmaktır

4
Varsayılanı neden timezone.now olarak ayarladınız, ancak pre_save sinyali datetime.datetime.now kullanıyor?
17'de Bobort

32

Bir yan soru hakkında konuşmak: bu alanları yönetici olarak görmek istiyorsanız (ancak, düzenleyemezsiniz), readonly_fieldsyönetici sınıfınıza ekleyebilirsiniz .

class SomeAdmin(ModelAdmin):
    readonly_fields = ("created","modified",)

Bu sadece en son Django sürümleri için geçerlidir (sanırım 1.3 ve üstü)


3
Dikkat edilmesi gereken nokta: bu XxAdminsınıfa eklenmelidir . Çok çabuk okudum ve derslerime AdminFormya da ModelFormderslerime eklemeye çalıştım ve neden "salt okunur alanları" oluşturmadıklarını bilmiyordum. BTW, bir formda gerçek "salt okunur alanlar olması mümkün mü?
Tomasz Gandor

28

Bence buradaki en kolay (ve belki de en zarif) çözüm default, bir çağrılabilir olarak ayarlayabileceğiniz gerçeğinden yararlanmaktır . Bu nedenle, yöneticinin auto_now özel işlemesi hakkında bilgi almak için, alanı şu şekilde beyan edebilirsiniz:

from django.utils import timezone
date_filed = models.DateField(default=timezone.now)

timezone.now()Varsayılan değer güncellenmeyeceği için kullanmamanız önemlidir (örn. Varsayılan yalnızca kod yüklendiğinde ayarlanır). Kendinizi bunu çok fazla yaparsanız, özel bir alan oluşturabilirsiniz. Ancak, bu zaten sanırım oldukça KURU.


2
Varsayılan değer, auto_now_add öğesine (nesne ilk kaydedildiğinde ayarlanan değer) az ya da çok eşdeğerdir, ancak auto_now (nesne her kaydedildiğinde ayarlanan değer) gibi değildir.
Shai Berger

1
@ShaiBerger, bence önemli ölçüde incelikli farklılar. Doküman inceliği şöyle ifade etti: "Alanı otomatik olarak ayarla ...; yalnızca geçersiz kılabileceğiniz varsayılan bir değer değil." - docs.djangoproject.com/tr/dev/ref/models/fields/…
Thomas - BeeDesk

@ Thomas-BeeDesk: Kabul etti. Bu nedenle, "az ya da çok eşdeğer".
Shai Berger

1
Taşıma kullanıyorsanız bu çözüm kötü çalışır. Her çalıştırdığınızda makemigrationsvarsayılanı çalıştırdığınız zaman olarak yorumlar makemigrationsve bu nedenle varsayılan değerin değiştiğini düşünür!
nhinkle

8
@nhinkle, default=timezone.now()önerilenlerden ziyade belirtmediğinizden emin misiniz : default=timezine.now(parantez yok)?
Josh

18

Model sınıfınızı şu şekilde değiştirirseniz:

class MyModel(models.Model):
    time = models.DateTimeField(auto_now_add=True)
    time.editable = True

Ardından bu alan yönetici değişiklik sayfamda görünecek


1
Ancak YALNIZCA düzenleme kaydında çalışır. Yeni kayıt oluşturduğumda - güncel karo değeri göz ardı edildi. Bu kaydı değiştirdiğimde - yeni değer ayarlandı.
Anton Danilchenko

2
Çalışır, ancak modeller yerine sürülmelidir.DateTimeField, modeller yerine.DatetimeField
matyas

2
başarısız oldu python manage.py makemigrations: KeyError: u'editable '
laoyur

12

Ne okuduğum ve Django ile yaşadığım deneyime dayanarak, auto_now_add buggy. Jthanism ile aynı fikirdeyim --- normal kaydetme yöntemini geçersiz kılar temiz ve ne olduğunu biliyorsunuz. Şimdi, kuru hale getirmek için TimeStamped adlı soyut bir model oluşturun:

from django.utils import timezone

class TimeStamped(models.Model):
    creation_date = models.DateTimeField(editable=False)
    last_modified = models.DateTimeField(editable=False)

    def save(self, *args, **kwargs):
        if not self.creation_date:
            self.creation_date = timezone.now()

        self.last_modified = timezone.now()
        return super(TimeStamped, self).save(*args, **kwargs)

    class Meta:
        abstract = True

Ve sonra, bu zaman damgası davranışına sahip bir model istediğinizde, sadece alt sınıf:

MyNewTimeStampyModel(TimeStamped):
    field1 = ...

Alanların yönetici olarak görünmesini istiyorsanız, editable=Falseseçeneği kaldırın


1
Hangisini timezone.now()burada kullanıyorsun? Varsayıyorum django.utils.timezone.now(), ama olumlu değilim. Ayrıca, neden kullanmak timezone.now()yerine datetime.datetime.now()?
coredumperror

1
Güzel nokta. İçe aktarma ifadesini ekledim. Bunun nedeni timezone.now()saat diliminin farkında olması, saat datetime.datetime.now()diliminin naif olmasıdır. Burada okuyabilirsiniz: docs.djangoproject.com/en/dev/topics/i18n/timezones
Edward Newell

@EdwardNewell Neden default=timezone.nowfield kurucu içinde değil, create_date öğesini kayıtta ayarlamayı seçtiniz ?
Blackeagle52

Hmm .. belki de hiç düşünmedim, kulağa daha iyi geliyor.
Edward Newell

2
Last_modified'in güncellenmeyeceği bir durum var: update_fieldsarg sağlandığında ve 'last_modified' listede olmadığında şunu ekleyeceğim:if 'update_fields' in kwargs and 'last_modifed' not in kwargs['update_fields']: kwargs['update_fields'].append('last_modified')
danius

5

Bu endişe kaynağı mı?

Hayır, Django modelleri kaydederken sizin için otomatik olarak ekler, bu yüzden beklenir.

Yan soru: Yönetici aracımda, bu 2 alan görünmüyor. Bu beklenen mi?

Bu alanlar otomatik olarak eklendiğinden, gösterilmezler.

Yukarıdakilere eklemek için, synack'in dediği gibi, django posta listesinde bunu kaldırmak için bir tartışma yapıldı, çünkü "iyi tasarlanmadı" ve "hack"

Modellerimin her birine özel bir save () yazmak auto_now kullanmaktan çok daha acı verici

Açıkçası her modele yazmak zorunda değilsiniz. Bir modele yazabilir ve diğerlerini modelden devralabilirsiniz.

Fakat, auto_addve auto_now_addorada, ben bir yöntemini kendim yazmaya çalışıyorum yerine bunları kullanmak.


3

Bugün işte benzer bir şeye ihtiyacım vardı. Varsayılan değer olmakla birlikte timezone.now(), hem yönetici hem de sınıf görünümlerinde devralınacak şekilde düzenlenebilir FormMixin, bu nedenle models.pyaşağıdaki kodumda oluşturulan bu gereksinimleri karşıladı:

from __future__ import unicode_literals
import datetime

from django.db import models
from django.utils.functional import lazy
from django.utils.timezone import localtime, now

def get_timezone_aware_now_date():
    return localtime(now()).date()

class TestDate(models.Model):
    created = models.DateField(default=lazy(
        get_timezone_aware_now_date, datetime.date)()
    )

İçin DateTimeField, ben kaldırmak tahmin .date()fonksiyonu ve değişimden datetime.dateiçin datetime.datetimeveya daha iyi timezone.datetime. Ben bunu denemedim DateTime, yalnızca sona Date.


2

timezone.now()Oluşturulan ve auto_nowdeğiştirilenler için kullanabilirsiniz :

from django.utils import timezone
class User(models.Model):
    created = models.DateTimeField(default=timezone.now())
    modified = models.DateTimeField(auto_now=True)

Eğer varsayılan yerine özel bir birincil anahtar kullanıyorsanız auto- increment int, auto_now_addbir hata yol açacaktır.

Django'nun varsayılan DateTimeField.pre_saveauto_now ve ile kodu auto_now_add:

def pre_save(self, model_instance, add):
    if self.auto_now or (self.auto_now_add and add):
        value = timezone.now()
        setattr(model_instance, self.attname, value)
        return value
    else:
        return super(DateTimeField, self).pre_save(model_instance, add)

Parametrenin ne olduğundan emin değilim add. Umarım şöyle bir şey olur:

add = True if getattr(model_instance, 'id') else False

Yeni kaydın attr değeri olmayacak id, bu nedenle getattr(model_instance, 'id')False döndürecektir, alanda herhangi bir değer ayarlanmamasına neden olacaktır.


7
Varsayılanı timezone.now () olarak tutarsak, değişiklik yaptığınızda gerçek tarih ve saatin (bu anın) taşıma dosyasına aktarıldığını fark ettim. Bence bundan kaçınmalıyız çünkü makemigrations dediğinde bu alanın değeri farklı olacaktır.
Karan Kumar

2

Yönetici ekranınıza gelince, bu yanıta bakın .

Not: auto_nowve varsayılan auto_now_addolarak ayarlıdır editable=False, bu yüzden bu geçerlidir.


1

auto_now=TrueDjango 1.4.1'de benim için çalışmadı, ancak aşağıdaki kod beni kurtardı. Bu saat dilimi farkında tarih için.

from django.utils.timezone import get_current_timezone
from datetime import datetime

class EntryVote(models.Model):
    voted_on = models.DateTimeField(auto_now=True)

    def save(self, *args, **kwargs):
        self.voted_on = datetime.now().replace(tzinfo=get_current_timezone())
        super(EntryVote, self).save(*args, **kwargs)

1
class Feedback(models.Model):
   feedback = models.CharField(max_length=100)
   created = models.DateTimeField(auto_now_add=True)
   updated = models.DateTimeField(auto_now=True)

Burada, oluşturulduğunda ve bir kullanıcı geri bildirimi değiştirdiğinde zaman damgası olacak sütunlar oluşturduk ve güncelledik.

auto_now_add , bir örnek oluşturulduğunda zamanı ayarlarken auto_now , birisinin geri bildirimini değiştirdiği zamanı belirler.


-1

Güneyi kullanıyorsanız ve alanı veritabanına eklediğiniz tarihe varsayılan olarak ayarlamak istiyorsanız yanıt aşağıdadır:

2. seçeneği seçin ve ardından: datetime.datetime.now ()

Buna benzer:

$ ./manage.py schemamigration myapp --auto
 ? The field 'User.created_date' does not have a default specified, yet is NOT NULL.
 ? Since you are adding this field, you MUST specify a default
 ? value to use for existing rows. Would you like to:
 ?  1. Quit now, and add a default to the field in models.py
 ?  2. Specify a one-off value to use for existing columns now
 ? Please select a choice: 2
 ? Please enter Python code for your one-off default value.
 ? The datetime module is available, so you can do e.g. datetime.date.today()
 >>> datetime.datetime.now()
 + Added field created_date on myapp.User

güncellendi bu olacak: datetime ve django.utils.timezone modülleri kullanılabilir, bu yüzden örneğin timezone.now ()
michel.iamit 18:15

Bu, modelinizde önemli veriler olmadığında bir ankettir. Modelinizi doğru şekilde ayarlarsanız, bu istemi asla görmeniz gerekmez.
Shayne
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.