1

I am doing ajax validation when a user signs up for my application to see if the username already exists. The request is made whenever you unfocus/blur the username field. Locally I am using MySQL and on Heroku it's Postgres, so I'm assuming the issue lies there somehow because this functions perfectly locally on my dev machine.

The username registered is Matt for example...

Development:

  • Matt = taken is true
  • matt = taken is true
  • MATT = taken is true
  • MaTt = taken is true, etc...

Production:

  • Matt = taken is true
  • matt = taken is false
  • MATT = taken is false
  • MaTt = taken is false

And here is the method (you can even see I went a step further to try and force it to downcase, again works locally how it should, but not on production)...

def checkname

  scrubb = ActionController::Base.helpers.sanitize(params[:username], :tags => '')
  user = User.find_by_username(scrubb, :conditions => [ "lower(username) = ?", scrubb.downcase ])

  if user.blank?
    render :json => { :isAvailable => true }
  else
    render :json => { :isAvailable => false }
  end

  return

end

**EDIT**

This is the output of the generated MySQL query:

SELECT `users`.* FROM `users` WHERE `users`.`username` = 'MATT' AND (lower(username) = 'matt') LIMIT 1

So, it looks like the real issue is that AND statement Rails is generating. How would I remove that?

4

2 回答 2

1

您使用错误的方法来检查用户名。这个:

User.find_by_username(scrubb)

只是产生此 SQL 的一种方法:

select * from users where username = ...

然后你添加一些:conditions

:conditions => [ "lower(username) = ?", scrubb.downcase ]

并将find_by_username这些附加到它通常使用的 WHERE 子句中。结果是:

User.find_by_username(scrubb, :conditions => ...)

方法:

找出Userusername是真的scrubb,哪里:conditions是真的。

在 MySQL 中,字符串比较:

`users`.`username` = 'MATT'

在您的配置中显然不区分大小写(我相信默认配置),但在 PostgreSQL 中它将区分大小写;如果usernameis 'Matt',那么该条件在 PostgreSQL 中将失败,除了区分大小写的匹配项之外,您将找不到任何其他内容。

不要find_by_username用于此(或任何其他,如果您想username不区分大小写),请使用whereand count

n = User.where('lower(username) = ?', scrubb.downcase).count

或更好,where并且exists?

taken = User.where('lower(username) = ?', scrubb.downcase).exists?

添加范围User似乎也是一个好主意:

class User < ActiveRecord::Base
  def self.with_username(username)
    where('lower(username) = ?', username)
  end    
end

然后你可以说:

how_many = User.with_username(scrubb).count
taken    = User.with_username(scrubb).exists?

而且您不必在其他任何地方担心这种不区分大小写的问题。

并且请在开发和生产中使用相同的堆栈,还有各种其他的小差异会让您感到悲伤。

于 2013-08-24T23:46:29.700 回答
0

实际上,我自己通过一些对我来说更有意义的研究来解决这个问题。认为它可能会帮助其他人。

username = params[:username]
username.downcase
@user = User.where(User.arel_table[:username].matches("%#{username}%")).first

使用Arelandmatches将对ILIKEPostgres 和LIKE其他所有内容(source)使用运算符。然后,调用first以便您接收用户的单个对象而不是集合(source)。

在 MySQL 和 Postgres 中完全按照它应该的方式工作。我知道拥有不同的生产和开发环境是不好的做法……故事的寓意。我很快就会解决这个问题。

注意:在不区分大小写的话题上,我还发现了一个非常有用的 gem,叫做route_downcaser,你可能会感兴趣。

于 2013-11-25T15:21:47.563 回答