Thay đổi loại cột thành chuỗi dài hơn trong đường ray


90

Ở lần di chuyển đầu tiên, tôi đã khai báo trên một cột content là chuỗi Activerecord làm cho nó trở thành chuỗi (255) theo một gem annotate.

Sau khi tôi đẩy ứng dụng lên heroku, sử dụng postgres, nếu tôi nhập vào biểu mẫu trong nội dung một chuỗi dài hơn 255, tôi sẽ gặp lỗi

PGError: ERROR: value too long for type character varying(255)

Vấn đề là tôi cần nội dung đó chứa một chuỗi có lẽ rất dài (văn bản miễn phí, có thể là hàng nghìn ký tự)

  1. Biến nào (là chuỗi không thích hợp cho điều này) sẽ chấp nhận pg?
  2. Làm cách nào để tạo sự di chuyển để thay thế loại cột đó

cảm ơn

Câu trả lời:


216

Bạn nên sử dụng textvới Rails nếu bạn muốn một chuỗi không có giới hạn độ dài. Một cuộc di chuyển như thế này:

def up
  change_column :your_table, :your_column, :text
end
def down
  # This might cause trouble if you have strings longer
  # than 255 characters.
  change_column :your_table, :your_column, :string
end

nên sắp xếp mọi thứ. Bạn có thể muốn :null => falsehoặc một số tùy chọn khác ở cuối điều đó.

Khi bạn sử dụng một stringcột không có giới hạn rõ ràng, Rails sẽ thêm một giới hạn ngầm định :limit => 255. Nhưng nếu bạn sử dụng text, bạn sẽ nhận được bất kỳ kiểu chuỗi dài tùy ý nào mà cơ sở dữ liệu hỗ trợ. PostgreSQL cho phép bạn sử dụng một varcharcột không có độ dài nhưng hầu hết các cơ sở dữ liệu đều sử dụng một kiểu riêng cho cột đó và Rails không biết về việc varcharkhông có độ dài. Bạn phải sử dụng texttrong Rails để lấy một textcột trong PostgreSQL. Không có sự khác biệt trong PostgreSQL giữa một cột kiểu textvà một trong những loại varchar(nhưng varchar(n) khác nhau). Hơn nữa, nếu bạn đang triển khai trên PostgreSQL, không có lý do gì để sử dụng :string(AKA giống nhau trong nội bộ ngoại trừ các ràng buộc về độ dài bổ sung cho ; bạn chỉ nên sử dụngvarchar ) cả, cơ sở dữ liệu xử lý textvarchar(n)varchar(n)varchar(n) (AKA :string ) nếu bạn có một ràng buộc bên ngoài (chẳng hạn như biểu mẫu chính phủ cho biết rằng trường 432 trên biểu mẫu 897 / B sẽ dài 23 ký tự) trên kích thước cột.

Ngoài ra, nếu bạn đang sử dụng một stringcột ở bất kỳ đâu, bạn phải luôn chỉ định cột :limitnhư một lời nhắc nhở bản thân rằng có một giới hạn và bạn nên xác nhận trong mô hình để đảm bảo rằng giới hạn không bị vượt quá. Nếu bạn vượt quá giới hạn, PostgreSQL sẽ phàn nàn và đưa ra một ngoại lệ, MySQL sẽ lặng lẽ cắt ngắn chuỗi hoặc khiếu nại (tùy thuộc vào cấu hình máy chủ), SQLite sẽ để nó vượt qua nguyên trạng và các cơ sở dữ liệu khác sẽ làm điều gì đó khác (có thể là phàn nàn) .

Ngoài ra, bạn cũng nên phát triển, thử nghiệm và triển khai trên cùng một cơ sở dữ liệu (thường sẽ là PostgreSQL tại Heroku), thậm chí bạn nên sử dụng các phiên bản tương tự của máy chủ cơ sở dữ liệu. Có những khác biệt khác giữa các cơ sở dữ liệu (chẳng hạn như hành vi của GROUP BY) mà ActiveRecord sẽ không cách ly bạn. Bạn có thể đã làm điều này rồi nhưng tôi nghĩ dù sao thì tôi cũng sẽ đề cập đến nó.


13
Câu trả lời chính xác. Một lưu ý: Rails hiện không hỗ trợ change_column với phương thức thay đổi ( Guide.rubyonrails.org/migrations.html#using-the-change-method ); nếu bộ nhớ phục vụ, bạn sẽ tạo ra một sự di chuyển không thể đảo ngược nếu bạn làm điều đó. Tốt hơn là làm theo cách cũ với các phương pháp lên / xuống.
thơ,

@BourbonJockey: Sẽ có lý khi changekhông thể tự động đảo ngược thay đổi kiểu và Hướng dẫn di chuyển nói rằng "[phương pháp thay đổi] Phương pháp này được ưu tiên để viết di chuyển có tính xây dựng (thêm cột hoặc bảng)" và change_columnisn ' t trong danh sách bạn chỉ vào nên tôi nghĩ bạn đúng. Tôi đã sửa nó để sử dụng up/ down(có báo trước down), cảm ơn các bạn đã chú ý.
mu quá ngắn

4
Để các độc giả khác tham khảo trong tương lai, chuyển đổi từ chuỗi thành văn bản trong Postgres trên Heroku theo cách này sẽ KHÔNG làm mất dữ liệu.
Marina Martin

2
@Dennis: Có lẽ "bạn nên phát triển, thử nghiệm và triển khai bằng cách sử dụng cùng một cơ sở dữ liệu" sẽ chính xác hơn. Vấn đề thông thường là mọi người sử dụng thiết lập SQLite mặc định (vô lý) của Rails và mọi thứ sẽ sụp đổ khi họ triển khai bên trên một thứ khác. PostgreSQL vẫn là tùy chọn mặc định và phổ biến nhất tại Heroku, phải không?
mu quá ngắn

3
Một lưu ý nhỏ, giả định của Rails rằng các trường có độ dài không xác định phải là 255 ký tự là điều kỳ lạ. Trong PostgreSQL, không cần thiết phải sử dụng textchỉ để có độ dài không giới hạn; bạn chỉ có thể sử dụng không bị hạn chế varchar. Rails đang áp đặt giới hạn kỳ lạ này, không phải PostgreSQL.
Craig Ringer

8

Mặc dù câu trả lời được chấp nhận là tuyệt vời, tôi muốn thêm một câu trả lời ở đây hy vọng sẽ giải quyết tốt hơn câu hỏi áp phích gốc phần 2, cho những người không phải là chuyên gia như tôi.

  1. Làm cách nào để tạo sự di chuyển để thay thế loại cột đó

tạo ra sự di chuyển đoạn đầu

Bạn có thể tạo một sự di chuyển để giữ thay đổi của mình bằng cách nhập vào bảng điều khiển (chỉ cần thay thế tablecho tên bảng của bạn và columncho tên cột của bạn)

rails generate migration change_table_column

Thao tác này sẽ tạo ra sự di chuyển khung bên trong thư mục ứng dụng Rails / db / migrate / của bạn. Di chuyển này là một trình giữ chỗ cho mã di chuyển của bạn.

Ví dụ: tôi muốn tạo một sự di chuyển để thay đổi loại cột từ stringthành text, trong bảng có tên TodoItems:

class ChangeTodoItemsDescription < ActiveRecord::Migration
  def change
     # enter code here
     change_column :todo_items, :description, :text
  end
end

Chạy quá trình di chuyển của bạn

Khi bạn đã nhập mã để thay đổi cột vừa chạy:

rake db:migrate

Để áp dụng việc di chuyển của bạn. Nếu bạn mắc lỗi, bạn luôn có thể hoàn nguyên thay đổi bằng:

rake db:rollack

Phương pháp lên và xuống

Các tham chiếu UpDownphương pháp câu trả lời được chấp nhận , thay vì Changephương pháp mới hơn . Vì rails 3.2 kiểu cũ Phương pháp Lên và Xuống có một số ưu điểm so với phương pháp Thay đổi mới hơn. 'Lên và Xuống' tránh ActiveRecord::IrreversibleMigration exception. Kể từ khi phát hành Rails 4, bạn có thể sử dụng reversibleđể tránh lỗi này:

class ChangeProductsPrice < ActiveRecord::Migration
  def change
    reversible do |dir|
      change_table :products do |t|
        dir.up   { t.change :price, :string }
        dir.down { t.change :price, :integer }
      end
    end
  end
end

Thưởng thức Rails :)

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.