2010-03-12 8 views
5

Chcę być w stanie zrobić Artist.case_insensitive_find_or_create_by_name(artist_name) [1] (i mają działać zarówno SQLite oraz PostgreSQL)Case-niewrażliwy find_or_create_by_whatever

Jaki jest najlepszy sposób, aby osiągnąć ten cel? Teraz jestem po prostu dodanie metody bezpośrednio do Artist klasy (rodzaj brzydki, szczególnie jeśli chcę tę funkcję w innej klasy, ale cokolwiek):

def self.case_insensitive_find_or_create_by_name(name) 
    first(:conditions => ['UPPER(name) = UPPER(?)', name]) || create(:name => name) 
    end 

[1]: Dobrze, idealnie byłoby Artist.find_or_create_by_name(artist_name, :case_sensitive => false), ale wydaje się to znacznie trudniejsze do wdrożenia

+0

Dlaczego 'Artist.find_or_create_by_name (artist_name,: case_sensitive => false)' trudniejsze do wdrożenia? –

+1

Jeśli używałeś MySQL, dopasowania nie uwzględniają wielkości liter. –

+0

@KandadaBoggu, ponieważ 'find_or_create_by_name' jest tworzone dynamicznie przez' method_missing'? Może nie jest trudniej - jak byś to zaimplementował? –

Odpowiedz

7

Ta odpowiedź jest dla dodatkowych pytań w komentarzach zapytania.

Nie można wywołać wartości domyślnej find_or_create_by_name, jeśli zastąpi się tę metodę. Ale można zaimplementować własną rękę, jak pokazano poniżej:

def self.find_or_create_by_name(*args) 
    options = args.extract_options! 
    options[:name] = args[0] if args[0].is_a?(String) 
    case_sensitive = options.delete(:case_sensitive) 
    conditions = case_sensitive ? ['name = ?', options[:name]] : 
           ['UPPER(name) = ?', options[:name].upcase] 
    first(:conditions => conditions) || create(options) 
end 

Teraz można wywołać metodę zastąpiona następująco:

User.find_or_create_by_name("jack") 
User.find_or_create_by_name("jack", :case_sensitive => true) 
User.find_or_create_by_name("jack", :city=> "XXX", :zip => "1234") 
User.find_or_create_by_name("jack", :zip => "1234", :case_sensitive => true) 
1

Mówiono o tym jednym here. Nikt nie był w stanie wymyślić rozwiązania lepszego niż twoje :)

5

Musisz utworzyć indeks na podstawie bazy danych.

postgreSQL

Tworzenie niższy indeks case na artist_name kolumnie.

CREATE INDEX lower_artists_name ON artists(lower(artist_name)) 

mySQL

wyszukiwania to przypadku odporne

sqlLite

Tworzenie indeksu artist_name kolumny parametru posortowanych

CREATE INDEX lower_artists_name ON artists(artist_name collate nocase) 

Teraz można użyć find_or_create w DB niezależny sposób:

find_or_create_by_artist_name(lower(artist_name)) 

Reference

PostgreSQL: Case insensitive search

sqlLite: Case insensitive search

10

Szyny 4 daje sposób, aby osiągnąć to samo:

Artist.where('lower(name) = ?', name.downcase).first_or_create(:name=>name)

+0

Wygląda na to, że ta metoda nadal istnieje w szynach 4.2, ale jest przestarzała. – Jared

+1

Wygląda na to, że nadal działa w Railsach 5.0 bez ostrzeżeń o wycofaniu. Gdzie widziałeś, że ta metoda została uznana za przestarzałą? –

+0

Mój zły, masz rację. Po prostu pomyślałem, że jest przestarzałe, ponieważ nie mogłem go znaleźć w dokumentacji. Okazuje się, że to po prostu nieudokumentowane. – Jared