【问题标题】:How to access data returned by .where ActiveRecord如何访问 .where ActiveRecord 返回的数据
【发布时间】:2013-06-02 15:12:55
【问题描述】:

我已经开始使用 ActiveRecord 来访问我的数据库,它非常有用。但是,访问ActiveRecord::Relation 对象内的数据似乎是一个挑战。我使用以下方式查询我的数据库:

post '/login' do
    session.clear
    r = User.where(:username => params["user"], :password => Digest::MD5.hexdigest(params["pass"]))
    if r.count == 1 
        session[:user] = r
        session[:user].delete(:password)
        status 200
    else
        status 401
    end
end 

在返回数据后,Ruby 会检查以确保只有一行,如果有,则将 r 对象的内容设置为哈希 session[:user]。然后,理想情况下,它应该从session[:user] 对象中删除密码数据,以便它只提供识别用户的详细信息。但是,使用此代码,我得到错误:wrong number of arguments (1 for 0) 指向.delete 行。那么,我怎样才能从这个对象中删除密码数据,或者将其转换为哈希,因为我知道我可以删除哈希格式的密码。

【问题讨论】:

  • 你可以使用 User.select('row1,row1').where(:username)...而不是删除属性。

标签: ruby activerecord sinatra


【解决方案1】:

您是在关系上调用 delete,而不是在哈希或用户中。

正如@sunny1304 所说,您可以选择要从数据库中获取的字段,而且您必须调用#first 才能仅检索一个条目。

r = User.where(:username => params["user"], :password => Digest::MD5.hexdigest(params["pass"])).select(:id, :username, :other_attrs).first

无论如何,您不应该将所有用户数据存储在会话中。您应该只保存 id,并且可能保存一些不会更改的内容(例如,如果用户名存在于您的应用程序中)。并查询每个请求的用户数据。

【讨论】:

  • 好的,感谢关于会话数据的提示。我一定会实现的。
【解决方案2】:

在重新识别用户时,我通常只在 session 对象中存储一个唯一属性,该属性不容易被篡改(在 Rails 中,会话存储在一个现已签名和加密的 cookie 中)。会话存储的数据量通常是有限的。

为此,将token 添加到我的用户表中,每次用户登录时都会重新生成该表并存储在会话中。通常(在 PostgreSQL 上)我会定义此类型为 uuid 的列,但您也可以使用 VARCHAR(128) 或类似名称。

如果用户现在返回,我只需发出一个

@current_user = User.where(token: session['user_token']).first

当用户注销时,我只是将用户的token作废:

@current_user.update_attribute :token, nil
session.clear

【讨论】:

  • 我已经像 Ismael 所说的那样使用用户 ID 实现了该功能。但是,当我使用 @current_user.first 专门获取名字时,我得到了这个:{"user":{"birthday":868282140,"email":"test@test.com","first":"Test","gender":1,"id":3,"last":"User","password":"passwordinMD5","username":"test"}
【解决方案3】:

ActiveRecord 查询返回一个关系,而不是数组或对象 - 想想selectcollect 等。session[:user] = User.all.select{|u| u.id == 1} 真的没有意义,不是吗?

所以,就像上面说的:

User.where(:id => 1).class == ActiveRecord::Relation 

同时

User.find(1).class == User

这样做是为了在您链接查询时,ActiveRecord 可以将它们转换为一个 SQL 查询,而不是获取许多记录,将它们转换为 Ruby 对象,然后在它们上运行 Ruby 命令。

举一个非常简单的例子:

$ User.where(:id => 1).count
   (0.5ms)  SELECT COUNT(*) FROM "users" WHERE "users"."id" = 1
=> 1

对比:

$ User.where(:id => 1).all.count
  User Load (0.6ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1
=> 1

.all 将 ActiveRecord::Relation 转换为 Ruby 对象数组,在本例中为 User

那你就可以了

User.find(1).update_attributes(:name => "Joseph")

user = User.find(1)
user.name = "Jack"
user.save

因为它是一个 Ruby 对象。

【讨论】: