Zmiana typu kolumny na dłuższe ciągi w szynach

90

Podczas pierwszej migracji zadeklarowałem w kolumnie, że contentma być ciągiem Activerecord, zgodnie z annotate gem, aby był ciągiem (255).

Po wypchnięciu aplikacji do heroku, która używa postgres, jeśli w treści wpisuję w formularzu ciąg dłuższy niż 255, pojawia się błąd

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

Problem polega na tym, że potrzebuję, aby ta zawartość zawierała ciąg, który jest być może bardzo długi (dowolny tekst, może zawierać tysiące znaków)

  1. Która zmienna (czy łańcuch nie jest do tego odpowiednia) zaakceptowałaby pg?
  2. Jak utworzyć migrację, aby zastąpić typ tej kolumny

dzięki

Nick Ginanto
źródło

Odpowiedzi:

217

Powinieneś używać textz Railsami, jeśli potrzebujesz łańcucha bez limitu długości. Taka migracja:

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

powinien załatwić sprawę. Możesz chcieć :null => falselub inne opcje na końcu tego.

Kiedy używasz stringkolumny bez jawnego ograniczenia, Railsy dodają niejawne :limit => 255. Ale jeśli używasz text, otrzymasz dowolny ciąg o dowolnej długości obsługiwany przez bazę danych. PostgreSQL pozwala na użycie varcharkolumny bez długości, ale większość baz danych używa do tego osobnego typu, a Railsy nie wiedzą o varcharbez długości. Musisz użyć textw Railsach, aby uzyskać textkolumnę w PostgreSQL. W PostgreSQL nie ma różnicy między kolumną typu texta kolumną typu varchar(ale varchar(n) jest inna). Ponadto, jeśli wdrażasz na bazie PostgreSQL, nie ma żadnego powodu, aby używać :string(AKA varchar), baza danych traktuje textivarchar(n)to samo wewnętrznie, z wyjątkiem dodatkowych ograniczeń długości dla varchar(n); Powinieneś używać varchar(n)(AKA :string) tylko wtedy, gdy masz zewnętrzne ograniczenie (takie jak rządowy formularz, który mówi, że pole 432 w formularzu 897 / B będzie miało 23 znaki) dotyczące rozmiaru kolumny.

Nawiasem mówiąc, jeśli używasz stringkolumny w dowolnym miejscu, powinieneś zawsze określić :limitjako przypomnienie sobie, że istnieje limit i powinieneś mieć walidację w modelu, aby upewnić się, że limit nie zostanie przekroczony. Jeśli przekroczysz limit, PostgreSQL zgłosi skargę i zgłosi wyjątek, MySQL po cichu skróci ciąg znaków lub narzeka (w zależności od konfiguracji serwera), SQLite pozwoli na to, jak jest, a inne bazy danych zrobią coś innego (prawdopodobnie narzekają) .

Powinieneś także programować, testować i wdrażać na tej samej bazie danych (zazwyczaj będzie to PostgreSQL w Heroku), powinieneś nawet używać tych samych wersji serwera bazy danych. Istnieją inne różnice między bazami danych (takie jak zachowanie GROUP BY), przed którymi ActiveRecord nie będzie Cię izolować. Może już to robisz, ale pomyślałem, że i tak o tym wspomnę.


Aktualizacja : nowsze wersje ActiveRecord rozumieją varcharbez ograniczeń, więc przynajmniej z PostgreSQL możesz powiedzieć:

change_column :your_table, :your_column, :string, limit: nil

zmienić varchar(n)kolumnę na varchar. texti varcharnadal są tym samym, jeśli chodzi o PostgreSQL, ale niektóre konstruktory formularzy będą traktować je inaczej: varchardostaje an, <input type="text">a textotrzymuje wiele linii <textarea>.

mu jest za krótkie
źródło
13
Świetna odpowiedź. Jedna uwaga: Railsy obecnie nie obsługują change_column z metodą change ( guide.rubyonrails.org/migrations.html#using-the-change-method ); jeśli pamięć nam nie służy, stworzysz nieodwracalną migrację, jeśli to zrobisz. Lepiej zrobić to w staroświecki sposób, stosując metody up / down.
góra poetycka
@BourbonJockey: To ma sens, changeże nie byłoby w stanie automatycznie cofnąć zmiany typu, a Przewodnik po migracji mówi, że „[metoda zmiany] Ta metoda jest preferowana do pisania konstruktywnych migracji (dodawania kolumn lub tabel)” i change_columnnie jest t na liście, na którą wskazujesz, więc myślę, że masz rację. Naprawiłem to tak, aby używać up/ down(z zastrzeżeniem na down), dzięki za ostrzeżenie .
mu jest za krótkie
4
Dla przyszłego odniesienia dla innych czytelników, konwersja z ciągu znaków na tekst w Postgres na Heroku w ten sposób NIE spowoduje utraty danych.
Marina Martin
2
@Dennis: Być może bardziej dokładne byłoby sformułowanie „powinieneś programować, testować i wdrażać przy użyciu tej samej bazy danych”. Typowy problem polega na tym, że ludzie używają (absurdalnej) domyślnej konfiguracji SQLite w Railsach, a rzeczy rozpadają się, gdy instalują się na czymś innym. PostgreSQL jest nadal domyślną i najpopularniejszą opcją w Heroku, prawda?
mu jest za krótkie
3
Na marginesie, założenie Railsów, że pola o nieokreślonej długości powinny mieć 255 znaków, jest dziwne. W PostgreSQL nie jest konieczne używanie texttylko do uzyskania nieograniczonej długości; możesz po prostu użyć nieograniczonego varchar. Railsy narzucają ten dziwny limit, a nie PostgreSQL.
Craig Ringer
8

Chociaż przyjęta odpowiedź jest doskonała, chciałem dodać tutaj odpowiedź, która, mam nadzieję, lepiej radzi sobie z oryginalnym pytaniem dotyczącym plakatów część 2, dla nie-ekspertów, takich jak ja.

  1. Jak utworzyć migrację, aby zastąpić typ tej kolumny

generowanie migracji rusztowań

Możesz wygenerować migrację, aby zatrzymać zmiany, wpisując w konsoli (po prostu zamień tablena nazwę swojej tabeli i columnnazwę kolumny)

rails generate migration change_table_column

Spowoduje to wygenerowanie migracji szkieletu w aplikacji Rails / db / migrate / folder. Ta migracja jest symbolem zastępczym dla kodu migracji.

Na przykład chcę utworzyć migrację, aby zmienić typ kolumny z stringna text, w tabeli o nazwie TodoItems:

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

Prowadzenie migracji

Po wprowadzeniu kodu, aby zmienić kolumnę, po prostu uruchom:

rake db:migrate

Aby zastosować migrację. Jeśli popełnisz błąd, zawsze możesz cofnąć zmianę za pomocą:

rake db:rollack

Metody w górę iw dół

Zaakceptowane odniesienia Upi Downmetody odpowiedzi zamiast nowszej Changemetody. Od czasów railsów 3.2 metody Up i Down w starym stylu miały kilka zalet w porównaniu z nowszą metodą Change. Unikaj „w górę iw dół” ActiveRecord::IrreversibleMigration exception. Od wydania Rails 4 możesz użyć, reversibleaby uniknąć tego błędu:

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

Ciesz się Railsami :)

Tony Cronin
źródło