Làm cách nào để thay đổi cột nullable thành nullable trong di chuyển Rails?


188

Tôi đã tạo một cột ngày trong một lần di chuyển trước đó và đặt nó thành nullable. Bây giờ tôi muốn thay đổi nó thành không thể rỗng. Làm thế nào để tôi thực hiện điều này với giả định có hàng null trong cơ sở dữ liệu đó? Tôi ổn với việc đặt các cột đó thành Time.now nếu chúng hiện không có giá trị.

Câu trả lời:


204

Nếu bạn làm điều đó trong một di chuyển thì có lẽ bạn có thể làm điều đó như thế này:

# Make sure no null value exist
MyModel.where(date_column: nil).update_all(date_column: Time.now)

# Change the column to not allow null
change_column :my_models, :date_column, :datetime, null: false

1
Chỉ là một lưu ý, bởi vì điều này làm cho tôi phá vỡ cơ sở dữ liệu dev của tôi. Thay vì sử dụng cú pháp băm rõ ràng, như thế này : MyModel.update_all({:date_column => Time.now}, {:date_column => nil}). Truy vấn ở dạng ban đầu của bạn chỉ làm cho tất cả các mô hình của tôi có giá trị không trong trường.
dimitarvp

Cảm ơn các cập nhật. Tôi biết đây không phải là trường hợp khi tôi viết câu trả lời này nhưng tôi không thể nhớ phiên bản Ruby hoặc RoR nào tôi đang sử dụng vào thời điểm đó.
DanneManne

1
Bạn có sử dụng phương pháp 'lên' / 'xuống' trong di chuyển này không, hoặc bạn có thể phương pháp thay đổi đơn giản trong di chuyển không?
EE33

2
Các changephương pháp không phải là quá phù hợp cho trường hợp này bởi vì (1) update_allphương pháp sẽ được thực hiện trên cả hai di cư và một Revert tiềm năng. Đó có thể không phải là điều tồi tệ nhất nhưng bởi vì (2) việc di chuyển không có cách nào để biết cột được thay đổi từ đâu trong một hoàn nguyên tiềm năng. Vì vậy, trong trường hợp này tôi sẽ gắn bó với updown.
DanneManne

2
Đối với bất kỳ ai quan tâm, câu trả lời của tôi cho thấy làm thế nào để làm điều này trong một bước duy nhất.
Rick Smith

167

Trong Rails 4, đây là một giải pháp (DRYer) tốt hơn:

change_column_null :my_models, :date_column, false

Để đảm bảo không có bản ghi nào tồn tại với NULLcác giá trị trong cột đó, bạn có thể truyền tham số thứ tư, là giá trị mặc định để sử dụng cho các bản ghi có NULLgiá trị:

change_column_null :my_models, :date_column, false, Time.now

4
Điều này gây ra vấn đề khi bảng đã có giá trị null. Xem câu trả lời của tôi
Rick Smith

5
Cũng có sẵn trong 3.2. Cũng có tham số thứ 4 để đặt mặc định trong đó giá trị là null.
toxaq

1
Cộng 1 cho change_column_null. Tuy nhiên bình luận của Rick Smith ở trên chỉ ra một trường hợp rất hợp lệ.
0112

Cập nhật để thêm truy vấn để cập nhật giá trị null. Tham số thứ 4 (giá trị mặc định) chỉ hữu ích khi bạn thực sự muốn có mặc định cho các bản ghi trong tương lai.
mrbrdo

3
Trên thực tế, theo tài liệu Rails 4.2, tham số thứ 4 KHÔNG đặt giá trị mặc định cho các bản ghi trong tương lai: "Phương thức chấp nhận đối số thứ tư tùy chọn để thay thế + NULL + s bằng một số giá trị khác. Xin lưu ý đối số thứ tư không được đặt một cột mặc định. "
Mike Fischer

70

Rails 4 (các câu trả lời khác của Rails 4 có vấn đề):

def change
  change_column_null(:users, :admin, false, <put a default value here> )
  # change_column(:users, :admin, :string, :default => "")
end

Thay đổi một cột có giá trị NULL trong đó để không cho phép NULL sẽ gây ra sự cố. Đây chính xác là loại mã sẽ hoạt động tốt trong thiết lập phát triển của bạn và sau đó gặp sự cố khi bạn cố gắng triển khai nó vào sản xuất TRỰC TIẾP của mình . Trước tiên, bạn nên thay đổi giá trị NULL thành một cái gì đó hợp lệ và sau đó không cho phép NULL. Giá trị thứ 4 change_column_nullthực hiện chính xác điều đó. Xem tài liệu để biết thêm chi tiết.

Ngoài ra, tôi thường thích đặt giá trị mặc định cho trường vì vậy tôi sẽ không cần chỉ định giá trị của trường mỗi khi tôi tạo đối tượng mới. Tôi bao gồm các mã nhận xét để làm điều đó là tốt.


3
Đối với Rails 4, đây có vẻ là câu trả lời chính xác và đầy đủ nhất, bao gồm cả cài đặt mặc định được nhận xét.
Mike Fischer

4
Nếu bạn đang thêm một cột mới vào một bảng và muốn chèn các giá trị mới cho null, nhưng không muốn thêm một giá trị mặc định cho cột, bạn có thể thực hiện điều này trong quá trình di chuyển của mình: add_column :users, :admin, :stringsau đóchange_column_null(:admin, :string, false, "new_value_for_existing_records")
colsen

34

Tạo một di chuyển có một change_columntuyên bố với một :default =>giá trị.

change_column :my_table, :my_column, :integer, :default => 0, :null => false

Xem: change_column

Tùy thuộc vào công cụ cơ sở dữ liệu mà bạn có thể cần sử dụng change_column_null


1
Điều này làm việc cho tôi. Sử dụng MySql cục bộ. Khi được đẩy và chạy ứng dụng trong Heroku (Postgres), nó xuất hiện trên cột không phải là null khi tôi viết nó là null - đúng như vậy. Chỉ "change_column_null" không thể sử dụng "change_column ...: null => false" trên MySql. Cảm ơn.
rtfminc

1
vậy di chuyển của bạn là gì sau khi
thay đổi_column_null

1
Postges nghiêm ngặt hơn với MySQL - Tôi mong rằng nó sẽ yêu cầu change_column_null.
jessecurry

3
@rtfminc Tôi thực sự khuyên bạn nên sử dụng cùng một công cụ cơ sở dữ liệu trong quá trình phát triển và sản xuất, vì nó tránh được rất nhiều vấn đề khi gặp các trường hợp cạnh.
yagooar


3

Trong Rails 4.02+ theo các tài liệu , không có phương thức nào giống như update_allvới 2 đối số. Thay vào đó, người ta có thể sử dụng mã này:

# Make sure no null value exist
MyModel.where(date_column: nil).update_all(date_column: Time.now)

# Change the column to not allow null
change_column :my_models, :date_column, :datetime, null: false

2

Bạn không thể sử dụng add_timestamp và null: false nếu bạn có các bản ghi hiện có, vì vậy đây là giải pháp:

def change
  add_timestamps(:buttons, null: true)

  Button.find_each { |b| b.update(created_at: Time.zone.now, updated_at: Time.zone.now) }

  change_column_null(:buttons, :created_at, false)
  change_column_null(:buttons, :updated_at, false)
end
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.