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

2 komentarze:

  1. Jakim cudem unikalny klucz w bazie danych pozwoli na dodanie duplikatu?

    OdpowiedzUsuń
  2. Dziękuję za komentarz.
    Zawsze staram się rzeczowo odpowiadać na komentarze niezależnie od ich zabarwienia emocjonalnego, czy poziomu wiedzy komentującego. Tu też się tak postaram.
    Drogi Łukaszu nie chcę wnikać w strukturę i sposób działania tabel złączeń. Tuszę, że samouczek (http://apohllo.pl/guides/association_basics.html) i dokumentacja (http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#M002126) zrobi to o niebo lepiej ode mnie.

    OdpowiedzUsuń