不区分大小写的find_or_create_by_whatever

8
我希望能够执行Artist.case_insensitive_find_or_create_by_name(artist_name)[1](并且在sqlite和postgreSQL上都可以运行)
如何才能最好地实现这一点呢?目前,我只是直接向Artist类添加了一个方法(有点丑陋,特别是如果我想要这个功能在另一个类中,但无论如何):
  def self.case_insensitive_find_or_create_by_name(name)
    first(:conditions => ['UPPER(name) = UPPER(?)', name]) || create(:name => name)
  end

[1]: 好的,理想情况下应该是Artist.find_or_create_by_name(artist_name, :case_sensitive => false),但是这似乎更难实现。


为什么实现Artist.find_or_create_by_name(artist_name, :case_sensitive => false)更加困难? - Harish Shetty
1
如果您使用MySQL,匹配是不区分大小写的。 - Harish Shetty
@KandadaBoggu 因为 find_or_create_by_name 是由 method_missing 动态创建的吗?也许这并不难 -- 你会如何实现它? - Tom Lehman
像实现其他类方法一样实现它。如果您已经定义了此方法,则不会调用method_missing? - Harish Shetty
但是,如果用户没有使用 :case_sensitive => false,我该如何访问默认的 find_or_create_by_name 行为呢?(我猜复制它并不是什么大问题) - Tom Lehman
4个回答

18
Rails 4 提供了一种方式来实现同样的功能:
```ruby Artist.where('lower(name) = ?', name.downcase).first_or_create(:name=>name) ```

听起来这个方法在Rails 4.2中仍然存在,但已被弃用。 - Jared
2
看起来它在Rails 5.0中仍然可以正常工作,没有任何弃用警告。你在哪里看到这个方法已经被弃用了? - Erica Tripp
我的错,你是正确的。我只是认为它已经被弃用了,因为我在文档中找不到它。结果发现它只是未记录。 - Jared
在Rails 5.1上也能正常工作。 - Sandip Subedi

7
这个回答是针对问题评论中提出的额外问题。
如果你覆盖了默认的find_or_create_by_name方法,就无法调用它。但是,你可以根据以下示例实现自己的方法:
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

现在您可以按照以下方式调用已覆盖的方法:
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)

5

您需要基于数据库创建索引。

postgreSQL

artist_name 列上创建小写索引。

CREATE INDEX lower_artists_name ON artists(lower(artist_name))

MySQL

搜索不区分大小写。

SQLite

使用collate参数在artist_name列上创建索引。

CREATE INDEX lower_artists_name ON artists( artist_name collate nocase)

现在您可以以与数据库无关的方式使用find_or_create:
find_or_create_by_artist_name(lower(artist_name))

参考资料

PostgreSQL:大小写不敏感搜索

sqlLite:大小写不敏感搜索


1

关于这个问题,在这里有讨论。没有人能够提出比你更好的解决方案 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接