Modyfikacja bazy za pomocą migracji

Witam,
spotkałem się ostatnio z dziwną sytuacją, że operacja wykonywała się, jeżeli była w jednej migracji. Natomiast jeżeli zmiana została rozłożona z jakiegoś powodu na dwie migracje, to niestety już druga się nie wykonywała:

[code=“ruby”]#poprawnie wykonująca się migracja:
class AddTicketTypeToCategories < ActiveRecord::Migration
def self.up
add_column :categories, :ticket_type, :string
Category.reset_column_information
Category.all.each { |cat| cat.update_attribute :ticket_type, “ticket” }
end

def self.down
remove_column :categories, :ticket_type
end
end

#migracje, z których druga nie umieszczała danych w bazie
class AddTicketTypeToCategories < ActiveRecord::Migration
def self.up
add_column :categories, :ticket_type, :string
end

def self.down
remove_column :categories, :ticket_type
end
end

class AddTicketTypesToDevData < ActiveRecord::Migration
def self.up
Category.all.each { |cat| cat.update_attribute :ticket_type, “ticket” }
end

def self.down
Category.all.each { |cat| cat.update_attribute :ticket_type, NULL }
end
end[/code]
Nie jest to pierwsza sytuacja, w której z czymś takim się spotykam i zastanawiam się, czy to jest mój jakiś błąd, czy taki ficzer Railsów…
Pozdrawiam,
Yax

A te dwie migracje były w osobnych plikach czy w jednym pliku ? Jak w jednym to masz odpowiedż jak w osobnych to nie wiem :slight_smile:

Osobno oczywiście :wink:

Rails poprostu nie przeladowuje srodowiska miedzy migracjami, wiec nowe kolumny sa nie widoczne.
Polecam albo dodanie reset_column_infomation albo uzywanie metod ktore nie lukaja po atrybutch, tutaj z powodzeniem mozesz uzyc Category.update_all([‘ticket_type = ?’, ‘ticket’]).

Chyba nie do końca tak jest. Gdyby tak było to #update_attribute powinien rzucić wyjątkiem. reset_column_information było wymagane bodajże przed 1.2.6 teraz imho nie jest w takich wypadkach.

reset_column_information też sprawdzałem i nic. Nie próbowałem update_all, tylko update, też z negatywnym skutkiem. Zaraz sprawdzę update_all.

EDIT:
reset_column_information zadziałało. Wydawało mi się, że też to sprawdzałem. Moje przeoczenie w takim razie.

Niestety tak to pokracznie dziala, po wykonaniu

[code]class AddTicketTypeToCategories < ActiveRecord::Migration
def self.up
add_column :categories, :ticket_type, :string
Category.all.each { |cat| cat.update_attribute :ticket_type, “ticket” }
end

def self.down
remove_column :categories, :ticket_type
end
end[/code]
Nie dostaniesz wyjatku, tylko ticket_type nie zostanie zaktualizowane. Wiec reset_column_information jest nadal wymagane.

Tzn, zadziałało reset_column_information po wrzuceniu do drugiej migracji. A w jednej migracji jest wymagane, jeżeli chce się dodać dane. W dokumentacji bieżącej jest o tym mowa.

O, ktoś dodał moją notkę z ApiDock odnośnie reset_column_information do oficjalnej dokumentacji :slight_smile:

Czyli w najnowszych Railsach to wciąż jest wymagane ?, no coż, pewnie mi się kiedyś przyśniło, że nie jest :slight_smile:

Dziwne jest to, że trzeba to zrobić w migracji, gdzie kolumna była wprowadzona wcześniej.

Modyfikacja treści bazy danych nie jest dobrym pomysłem w migracjach.

Migracje powinny operować jedynie na schemacie bazy danych. Migracje NIE SĄ dobrym miejscem do tworzenia, usuwania czy modyfikowania rekordów, i to z różnych powodów.

W rails 3 jest bootstrapping, który jednak w rails 2.3 możesz sobie śmiało zastąpić taskiem rake i tam wykonywać operacje na bazie.

Jeszcze raz, cobyś zapamiętał:
MIGRACJE NIE SĄ DOBRYM MIEJSCEM DO MODYFIKACJI TREŚCI BAZY DANYCH.

:wink:

W rails 2.3.4 mamy do dyspozycji polecenie rake db:seed, które ładuje kod z pliku db/seeds.rb.

[code=ruby]# This file should contain all the record creation needed to seed the database with its default values.

The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).

Examples:

cities = City.create([{ :name => ‘Chicago’ }, { :name => ‘Copenhagen’ }])

Major.create(:name => ‘Daley’, :city => cities.first)[/code]

A co w przypadku np. zmiany typu kolumny / dodania nowej kolumny która trzeba zainicjować domyślną wartością generowaną na podstawie dynamicznych informacji? Ja bym to robił w migracji.Szczególnie przy modyfikowaniu bazy produkcyjnej - dzieki temu mozna sie wycofać.

Dokładnie, też tak czasem robię.

Na przykład dodaję nowe pole, które musi być wypełnione wartościami, które zależą od innych pól modelu. Przy tworzeniu nowych obiektów pole to zostanie wypełnione, ale co z obecną bazą danych? Najłatwiej to zrobić migracji.

@hubertlepicki
Tak, wiem, że w migracjach nie powinno się modyfikować danych, ale to są tylko moje dane żebym coś miał w bazie przy pisaniu aplikacji. Nie odniosę się do zmiany w ten sposób danych w bazie produkcyjnej, bo nigdy jeszcze takowej nie miałem :wink:

@morgoth
Ciekawa sprawa, choć te dane o których w moich migracjach mowa, to nie są wartości domyślne, tylko dane, żebym miał co klikać.

Nie lepiej, skoro robisz to na developmencie, po prostu machnąć mikroskrypt i nie zatruwać migracji (które potem pójdą przecież na produkcji) tego typu kodem?
Fajnie że seedy zostały backportowane do 2.3.4, już myślałem że trzeba będzie na 3.0 czekać :slight_smile:

Do tego celu ostatnio odkryłem gem populate - coś pięknego :wink:
http://railscasts.com/episodes/126-populating-a-database

Dokładnie, też się tego nie spodziewałem i dowiedziałem się z tego wątku :).

Osobiście znam jednago gagatka który nie tylko modyfikuje treść bazy z migracji, twierdzi również że najlepszym sposobem na tworzenie bazy w production jest użycie “rake db:reset” ;D. No jasne że jest, kiedy migracje nie chcą się zapuścić od v0 do ostatniej ;).