Bir geçişte oluşturma zamanını kaydetmek için bir tarih saat sütunu için varsayılan bir değer nasıl ayarlanır?


114

Aşağıdaki tablo oluşturma komut dosyasını düşünün:

create_table :foo do |t|
  t.datetime :starts_at, :null => false
end

Varsayılan değeri şimdiki zaman olarak ayarlamak mümkün mü?

Aşağıda verilen SQL sütun tanımları için raylarda DB'den bağımsız bir eşdeğer bulmaya çalışıyorum:

Oracle Sözdizimi

start_at DATE DEFAULT SYSDATE() 

MySQL Sözdizimi

start_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP

VEYA

start_at DATETIME DEFAULT NOW()

Yanıtlar:


174

Bu artık Rails 5'te desteklenmektedir.

İşte örnek bir geçiş:

class CreatePosts < ActiveRecord::Migration[5.0]
  def change
    create_table :posts do |t|
      t.datetime :modified_at, default: -> { 'CURRENT_TIMESTAMP' }
      t.timestamps
    end
  end 
end

Https://github.com/rails/rails/issues/27077 adresindeki tartışmaya bakın ve orada prathamesh-sonpatki tarafından yanıtlayın


14
Mükemmel cevap. Yalnızca bir kenara, Postgres'te CURRENT_TIMESTAMPgeçerli işlemin başlama zamanı olacağını unutmayın, bu nedenle aynı işlemde oluşturulan birden fazla kayıt aynı değeri alacaktır. İfadenin yürütüldüğü gerçek zamanı istiyorsanız (işlem içeriğini yok sayarak), kontrol edin CLOCK_TIMESTAMP.
Abe Voelker

add_column :table_name, :start_date, :datetime, default: -> { 'CURRENT_TIMESTAMP' }Şunun gibi bir şey ekledim: ve schema.rb'de göründüğü gibi görünüyor. t.datetime "start_date", default: -> { "now()" }Ama yeni bir kayıt oluşturduğumda, doldurulmuyor. Herhangi bir fikrin neden?
Sandip Subedi

@SandipSubedi benim için aynı. Raylarda 5.2.3.
courtimas

1
@courtsimas post.reloadbu değerleri elde etmek için yapmanız gerekenler . Burada açıklanmıştır: stackoverflow.com/questions/53804787/…
Sandip Subedi

1
@SandipSubedi Bunu yorumumdan 5 dakika sonra anladım. Aynı uuid neslinde olduğu gibi. Teşekkürler!
courtimas

119

Bunun gibi bir modele bir işlev ekleyebilirsiniz:

  before_create :set_foo_to_now
  def set_foo_to_now
    self.foo = Time.now
  end

Böylece model, modeldeki geçerli saati ayarlayacaktır.

Veritabanı düzeyinde varsayılan değeri ayarlamak için geçişe bazı sql kodu da yerleştirebilirsiniz, örneğin:

execute 'alter table foo alter column starts_at set default now()'

Bunun gibi bir şey ayarlamak:

create_table :foo do |t|
  t.datetime :starts_at, :null => false, :default => Time.now
end

taşıma sırasında Time.now işlevinin çalıştırılmasına neden olur, bu nedenle veritabanındaki tablo şu şekilde oluşturulur:

create table foo ( starts_at timestamp not null default '2009-01-01 00:00:00');

ama bence istediğin bu değil.


1
Şu anda: before_create geri aramasında değeri ayarlıyorum. <br> Burada bir tür AR sihri arıyordum. Rails koduna bakmak için biraz zaman harcadım ama herhangi bir çözüm bulamadım. Alternatif olup olmadığını görmek için etrafa soracağımı düşündüm.
Harish Shetty

Bunu before_create üzerinde bir geri arama ile yapmanızı öneririm.
jonnii

1
Kod DB'mi nötr tutmak istediğim için DB tablosunu değiştirmek istemiyorum. AR'nin, created_at alanına benzer Datetime alanı için varsayılan değeri ayarlamak için bazı mekanizmalara sahip olduğunu umuyordum.
Harish Shetty

9
Unutmayın before_create veritabanına ama sonra takmadan önce geri arama çalıştırır (ile sevdiği nesne örneğini new()). Yani self.foo = Time.nowverebileceğiniz değeri geçersiz kılacaktır new(). Onun self.foo = Time.current unless self.foo.present?yerine öneririm .
Fatih

2
Bu değil, execute kullanıldığında, bu :starts_at, :default => 'now()'schema.rb'ye bir satır koyacaktır. Ancak bu rake db:schema:dump, schema.rb'yi :starts_at, :default => '2015-05-29 09:46:33'(veya betiği başlattığınız tarih ne olursa olsun) geçersiz kılacak kullanılırken çalışmaz ... üzücü ....
astreal

13

Tabloda created_at/ created_onveya updated_at/ adlı alanlar varsa Active Record otomatik olarak zaman damgaları işlemleri oluşturur ve günceller updated_on. Kaynak - api.rubyonrails.org

O sütuna sahip olmak dışında başka bir şey yapmanıza gerek yok.


Bu alanlar zaten tablomda var. Planlayıcımın başlangıç ​​tarihini tutması için ek bir alana ihtiyacım var. Şu anda, geçerli tarihi ayarlamak için: before_create geri aramayı kullanıyorum. Bu senaryoyla sık sık karşılaşırsam, ColumnDefinition sınıfının 'to_sql' yöntemindeki varsayılan değer işlemeyi değiştirmek için bir eklenti yazmaya başvurmam gerekir.
Harish Shetty

10

Genellikle yaparım:

def change
  execute("
    ALTER TABLE your_table
    ALTER COLUMN your_column
    SET DEFAULT CURRENT_TIMESTAMP
  ")
end

Yani, sizin schema.rbgibi bir şeye sahip olacaksınız:

create_table "your_table", force: :cascade do |t|
  t.datetime "your_column", default: "now()"
end

Dezavantajı: Bu çözüm rake db:migrate, şema dosyanızı rake db:schema:load.
Lagos

9

Benzer bir çözüm arıyordum ama https://github.com/FooBarWidget/default_value_for kullanarak bitirdim .

default_value_forEklenti bir bildirge şekilde ActiveRecord modelleri için varsayılan değerleri tanımlamanızı sağlar. Örneğin:

class User < ActiveRecord::Base
  default_value_for :name, "(no name)"
  default_value_for :last_seen do
    Time.now
  end
end

u = User.new
u.name       # => "(no name)"
u.last_seen  # => Mon Sep 22 17:28:38 +0200 2008

8

Eğer gerekiyorsa değiştirmek bir varolan DateTime sütunu varsayılan tarih özelliği yararlanmak böylece (daha doğrusu diğer cevaplar belirtilen yeni bir tablo oluşturmak yerine) Raylar 5'de, böyle bir göç oluşturabilirsiniz:

class MakeStartsAtDefaultDateForFoo < ActiveRecord::Migration[5.0]
  def change
    change_column :foos, :starts_at, :datetime, default: -> { 'CURRENT_TIMESTAMP' }
  end
end

Bir şeyi reddetmek ve cevapla hangi sorunun alındığını açıklamamak için kötü bir form. Bunun neden reddedildiğine dair bir fikri olan var mı? Sütunu oluşturmak yerine değiştirmek istiyorsanız sözdizimini görüntüler.
Matt Long

Olumsuz oy veren ben değilim, ancak bu cevabın, sizden bir yıl öncesine kadar gelen iradenin cevabından nasıl farklı olduğunu görmekte güçlük çekiyorum. Soru bir varsayılan ayarlamakla ilgilidir ve cevabınız aynı lambda cümlesine sahiptir.
nurettin

2
@nurettin Ne demek istediğini anlıyorum, ama savunmamda, yeni bir sütun oluşturmanın sözdizimi, mevcut bir sütunu değiştirmekten çok farklı . Tümüyle yeni bir model / tablo oluşturmak yerine mevcut veri modellerine bir varsayılan eklemeye çalışırken bu soruyu web araması yoluyla bulanlara bu sözdizimini sağlamak muhtemelen oldukça yararlıdır. Hayır? Herkesin aynı lambdayı bir değişiklik_ sütunu için kullanabileceğini bildiğini varsayarsınız. Belki bunun farkına varmalılar, ama bu yüzden burada cevap verdim - bu yüzden bunu anlamak için başka bir yere gitmek zorunda değiller. Şerefe!
Matt Long

4
FWIW Şimdi bu sözdizimini kullandım ve Google aramam ve belirli bir problem için bu cevabın bana en çok yardımcı olduğunu takdir ediyorum.
Jay Killeen

1
Bunun bir yorumdan ziyade bir cevap olmasıyla ilgili yanlış bir şey görmüyorum. Upvoted. Bence olumsuz oy verenler dikkatsizce okuyorlar (soruların çoğu gibi!; P).
iconoclast

-1

@ Szymon-lipiński (Szymon Lipiński) tarafından verilen cevapta execute yöntemi bende işe yaramadı. MySQL sözdizimi hatası veriyordu.

Benim için çalışan MySQL sözdizimi budur.

execute "ALTER TABLE mytable CHANGE `column_name` `column_name` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP"

Bu nedenle, geçiş komut dosyasında bir tarih saat sütununun varsayılan değerini ayarlamak için şu şekilde yapılabilir:

def up
  create_table :foo do |t|
    t.datetime :starts_at, :null => false
  end

  execute "ALTER TABLE `foo` CHANGE `starts_at` `starts_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP"
end
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.