Bir Django model alanının varsayılan değeri bir işlev çağrısı / çağrılabilir olarak nasıl ayarlanır (örneğin, model nesnesi oluşturma zamanına göre bir tarih)


101

DÜZENLENEN:

Bir Django alanının varsayılanını, her yeni model nesnesi oluşturulduğunda değerlendirilecek bir işleve nasıl ayarlayabilirim?

Aşağıdakine benzer bir şey yapmak istiyorum, ancak bu kodda, bir model nesnesi her oluşturulduğunda kodu değerlendirmek yerine, kod bir kez değerlendirilir ve oluşturulan her model nesnesi için varsayılanı aynı tarihe ayarlar:

from datetime import datetime, timedelta
class MyModel(models.Model):
  # default to 1 day from now
  my_date = models.DateTimeField(default=datetime.now() + timedelta(days=1))



ORİJİNAL:

Bir işlev parametresi için dinamik olacak ve işlev her çağrıldığında çağrılacak ve ayarlanacak şekilde varsayılan bir değer oluşturmak istiyorum. Bunu nasıl yapabilirim? Örneğin,

from datetime import datetime
def mydate(date=datetime.now()):
  print date

mydate() 
mydate() # prints the same thing as the previous call; but I want it to be a newer value

Özellikle bunu Django'da yapmak istiyorum, ör.

from datetime import datetime, timedelta
class MyModel(models.Model):
  # default to 1 day from now
  my_date = models.DateTimeField(default=datetime.now() + timedelta(days=1))

Soru yanlış ifade edilmiş: işlev parametre varsayılanlarıyla hiçbir ilgisi yok. Cevabımı gör.
Ned Batchelder

@ NedBatchelder'ın yorumuna göre, sorumu düzenledim ("DÜZENLENDİ:" olarak belirtilir) ve orijinali bıraktım ("ORİJİNAL:" olarak belirtilir)
Rob Bednark

Yanıtlar:


140

Soru yanlış yönlendirilmiş. Django'da bir model alanı oluştururken, bir işlev tanımlamıyorsunuz, bu nedenle işlev varsayılan değerleri ilgisizdir:

from datetime import datetime, timedelta
class MyModel(models.Model):
  # default to 1 day from now
  my_date = models.DateTimeField(default=datetime.now() + timedelta(days=1))

Bu son satır bir işlevi tanımlamıyor; sınıfta bir alan oluşturmak için bir işlevi çağırıyor.

ÖN Django 1.7

Django , varsayılan olarak bir çağrılabilirliği iletmenize izin verir ve istediğiniz gibi her seferinde onu çağırır:

from datetime import datetime, timedelta
class MyModel(models.Model):
  # default to 1 day from now
  my_date = models.DateTimeField(default=lambda: datetime.now() + timedelta(days=1))

Django 1.7+

Lütfen Django 1.7'den beri, lambda'nın varsayılan değer olarak kullanılmasının tavsiye edilmediğini unutmayın (cf @stvnw comment). Bunu yapmanın uygun yolu , alandan önce bir işlev bildirmek ve onu varsayılan_değer adlı argümanında çağrılabilir olarak kullanmaktır:

from datetime import datetime, timedelta

# default to 1 day from now
def get_default_my_date():
  return datetime.now() + timedelta(days=1)

class MyModel(models.Model):
  my_date = models.DateTimeField(default=get_default_my_date)

Aşağıdaki @simanas cevabında daha fazla bilgi


2
Teşekkürler @NedBatchelder. Bu tam olarak aradığım şeydi. "Varsayılan" ın aranabilir olduğunu fark etmemiştim. Ve şimdi, fonksiyon çağırma ve fonksiyon tanımına ilişkin sorumun gerçekten nasıl yanlış yönlendirildiğini görüyorum.
Rob Bednark

25
Django> = 1.7 için, lambdas kullanımının alan seçenekleri için tavsiye edilmediğini unutmayın çünkü bunlar geçişlerle uyumlu değildir. Ref: Dokümanlar , bilet
StvnW

4
Djange> = 1.7 için yanlış olduğundan lütfen bu cevabın işaretini kaldırın. @Simanas'ın cevabı kesinlikle doğrudur ve bu nedenle kabul edilmelidir.
AplusKminus

Bu, geçişlerde nasıl çalışır? Tüm eski nesneler taşıma zamanına göre bir tarih alır mı? Taşıma işlemlerinde kullanılmak üzere farklı bir varsayılan ayarlamak mümkün müdür?
timthelion

3
Ya bir parametreyi aktarmak istersem get_default_my_date?
Sadan A.

59

Bunu yapmak default=datetime.now()+timedelta(days=1)kesinlikle yanlış!

Django örneğinizi başlattığınızda değerlendirilir. Apache'nin altındaysanız muhtemelen çalışacaktır, çünkü bazı yapılandırmalarda apache her istek üzerine django uygulamanızı iptal eder, ancak yine de bir gün kodunuzu gözden geçirip bunun neden beklediğiniz gibi hesaplanmadığını anlamaya çalışırken kendinizi bulabilirsiniz. .

Bunu yapmanın doğru yolu, çağrılabilir bir nesneyi varsayılan bağımsız değişkene iletmektir. Bir datetime.today işlevi veya özel işleviniz olabilir. Ardından her yeni varsayılan değer talep ettiğinizde değerlendirilir.

def get_deadline():
    return datetime.today() + timedelta(days=20)

class Bill(models.Model):
    name = models.CharField(max_length=50)
    customer = models.ForeignKey(User, related_name='bills')
    date = models.DateField(default=datetime.today)
    deadline = models.DateField(default=get_deadline)

21
Benim varsayılanım modeldeki başka bir alanın değerine dayanıyorsa, bu alanı bir parametre olarak geçirmek gibi bir şey mümkün müdür get_deadline(my_parameter)?
YPCrumble

@ YPCrumble bu yeni bir soru olmalı!
jsmedmar

2
Hayır! Bu mümkün değil. Bunu yapmak için farklı bir yaklaşım kullanmanız gerekecek.
Simanas

İşlevi deadlinesilerken alanı tekrar nasıl kaldırırsınız get_deadline? Varsayılan değer için işlev içeren bir alanı kaldırdım, ancak şimdi Django işlevi kaldırdıktan sonra başlangıçta çöküyor. Taşıma işlemini manuel olarak düzenleyebilirim, bu durumda bu sorun olmaz, ancak ya varsayılan işlevi değiştirip eski işlevi kaldırmak isteseydiniz?
beruic

8

Aşağıdaki iki DateTimeField yapıcısı arasında önemli bir ayrım vardır:

my_date = models.DateTimeField(auto_now=True)
my_date = models.DateTimeField(auto_now_add=True)

Eğer kullanırsanız auto_now_add=Trueyapıcısı içinde, my_date tarafından referans tarihi (sadece satır tabloya eklendiğinde kez ayarlanabilir) "değişmez" dir.

Bununla auto_now=Truebirlikte, tarih saat değeri, nesne her kaydedildiğinde güncellenecektir.

Bu kesinlikle benim için bir noktada bir aldatmaca oldu. Referans için dokümanlar burada:

https://docs.djangoproject.com/en/dev/ref/models/fields/#datetimefield


3
Auto_now ve auto_now_add tanımlarınızı karıştırdınız, tam tersi.
Michael Bates

@MichaelBates şimdi daha mı iyi?
Hendy Irawan

4

Bazen yeni bir kullanıcı modeli oluşturduktan sonra model verilerine erişmeniz gerekebilir.

Kullanıcı adlarının ilk 4 karakterini kullanarak her yeni kullanıcı profili için şu şekilde jeton oluşturabilirim:

from django.dispatch import receiver
class Profile(models.Model):
    auth_token = models.CharField(max_length=13, default=None, null=True, blank=True)


@receiver(post_save, sender=User) # this is called after a User model is saved.
def create_user_profile(sender, instance, created, **kwargs):
    if created: # only run the following if the profile is new
        new_profile = Profile.objects.create(user=instance)
        new_profile.create_auth_token()
        new_profile.save()

def create_auth_token(self):
    import random, string
    auth = self.user.username[:4] # get first 4 characters in user name
    self.auth_token =  auth + ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits + string.ascii_lowercase) for _ in range(random.randint(3, 5)))

3

Bunu doğrudan yapamazsınız; varsayılan değer, fonksiyon tanımı değerlendirildiğinde değerlendirilir. Ancak bunun etrafında iki yol var.

İlk olarak, her seferinde yeni bir işlev oluşturabilir (ve sonra çağırabilirsiniz).

Ya da daha basitçe, varsayılanı işaretlemek için özel bir değer kullanın. Örneğin:

from datetime import datetime
def mydate(date=None):
  if date is None:
    date = datetime.now()
  print date

Eğer Noneson derece mantıklı bir parametre değeri olduğunu ve onun yerine kullanabilirsiniz başka hiçbir makul değeri yok, sadece fonksiyonun etki alanının dışında kesinlikle var yeni bir değer oluşturabilir:

from datetime import datetime
class _MyDateDummyDefault(object):
  pass
def mydate(date=_MyDateDummyDefault):
  if date is _MyDateDummyDefault:
    date = datetime.now()
  print date
del _MyDateDummyDefault

Bazı nadir durumlarda, diyelim ki, kesinlikle her şeyi alabilmesi için gerçekten ihtiyaç duyan meta-kod yazıyorsunuz mydate.func_defaults[0]. Bu durumda, bunun gibi bir şey yapmanız gerekir:

def mydate(*args, **kw):
  if 'date' in kw:
    date = kw['date']
  elif len(args):
    date = args[0]
  else:
    date = datetime.now()
  print date

1
Kukla değer sınıfını gerçekten somutlaştırmak için hiçbir neden olmadığına dikkat edin - kukla değer olarak yalnızca sınıfın kendisini kullanın.
Amber

Bunu doğrudan YAPABİLİRSİNİZ, sadece işlev çağrısının sonucu yerine işlevi iletebilirsiniz. Gönderilen cevabımı görün.

Hayır yapamazsın. İşlevin sonucu, normal parametrelerle aynı türden bir şey değildir, bu nedenle bu normal parametreler için varsayılan bir değer olarak makul bir şekilde kullanılamaz.
abarnert

1
Evet, bir işlev yerine bir nesneyi iletirseniz, bir istisna ortaya çıkarır. Aynı şey, bir dizge bekleyen bir parametreye bir işlev iletirseniz de geçerlidir. Bunun ne kadar alakalı olduğunu anlamıyorum.

Alakalı, çünkü myfuncişlevi a almak (ve yazdırmak) için yapılmıştır datetime; onu değiştirdin, böylece bu şekilde kullanılamaz.
abarnert

2

İşlev çağrısının sonucunu iletmek yerine işlevi parametre olarak iletin.

Yani bunun yerine:

def myfunc(date=datetime.now()):
    print date

Bunu dene:

def myfunc(date=datetime.now):
    print date()

Bu işe yaramıyor, çünkü artık kullanıcı bir parametreyi gerçekten geçemiyor - onu bir işlev olarak çağırmaya ve bir istisna atmaya çalışacaksınız. Bu durumda, hiçbir print datetime.now()koşulsuz ve hiçbir koşulsuz kabul etmeyebilirsiniz .
abarnert

Dene. Bir tarih geçmiyorsunuz, datetime.now işlevini geçiyorsunuz.

Evet, varsayılan datetime.nowişlev geçmektir . Ancak, varsayılan olmayan bir değer ileten herhangi bir kullanıcı, bir işlev değil, bir tarih saat geçirecektir. foo = datetime.now(); myfunc(foo)yükselecek TypeError: 'datetime.datetime' object is not callable. İşlevinizi gerçekten kullanmak isteseydim, myfunc(lambda: foo)bunun yerine makul bir arayüz olmayan benzer bir şey yapmam gerekirdi .
abarnert

1
Fonksiyonları kabul eden arayüzler sıra dışı değildir. İsterseniz python stdlib'den örnekleri adlandırmaya başlayabilirim.

2
Django zaten bu sorunun önerdiği gibi bir çağrılabilir kabul ediyor.
Ned Batchelder
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.