piątek, 20 stycznia 2012

Jak wyeleminować duplikaty w tabelach złączeń

W standardowej asocjacji Rails zwanej has_and_belongs_to_many do utworzenia złączenia pomiędzy dwoma tabelami potrzebna jest trzecia tabela z parami kluczy id. Na przykład tabele firms i categories łączy tabela categories_firms. Włożenie do tej tabeli pary kluczy łączy daną firmę z kategorią. Problem w tym, że system dopuszcza wkładanie dowolnej ilości par kluczy. Pomaga w wielu wypadkach zastosowanie zapisu :uniq => true tak jak tu w modelu firm:
has_and_belongs_to_many :categories, :uniq => true
ale niestety nie zawsze.
Aby zabezpieczyć się przed umieszczaniem duplikatów najpierw robimy migrację, która przygotuje unikalny indeks zabezpieczający bazę danych przed zduplikowaniem wpisu:
.
add_index :categories_firms, [ :category_id, :firm_id ], :unique => true, :name => 'unique_by_category_and_firm'
.
Teraz co prawda nie da się zrobić duplikowanego wpisu, ale baza danych wywala błędy.
Przjdźmy więc do modelu firmy i tam dopiszmy do has_and_belongs_to_many:
.
has_and_belongs_to_many :categories, :uniq => true, :before_add => :validates_category
.
czyli zanim dokona się wpisu należałoby wykonać jakąś walidację:
def validates_category(category)
  raise ActiveRecord::Rollback if self.categories.include? category
end

Proste, łatwe i przyjemne.

Inspirację zaczerpnąłem z
http://stackoverflow.com/questions/4988630/habtm-uniqueness-constraint