为什么渲染json:返回NULL而不是boolean值

时间:2015-03-22 15:19:01

标签: ruby-on-rails

从API调用资源时,为什么我从 is_read 获取null而不是 true / false

否定:可能与render json:内部相关?

这是我第一次看到这个巫术所以请耐心等待。寻找好的答案:)

$ curl -X GET -H localhost:3000/api/v1/alerts/1/show | python -m json.tool


{
    "body": "Deserunt laboriosam quod consequuntur est dolor cum molestias.",
    "created_at": "2015-03-22T15:02:01.927Z",
    "id": 1,
    "is_read": null,
    "subtitle": "Aspernatur non voluptatem minus qui laudantium molestiae.",
    "title": "Et nemo magni autem similique consequuntur.",
    "updated_at": "2015-03-22T15:02:01.927Z"
}

-i

HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
Etag: "3232ec2058ffb13db1f9244366fe2ea8"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: c520cabe-6334-4997-956b-d1f21724b80c
X-Runtime: 0.016337
Server: WEBrick/1.3.1 (Ruby/2.1.2/2014-05-08)
Date: Sun, 22 Mar 2015 15:28:21 GMT
Content-Length: 300
Connection: Keep-Alive

Rails控制台:

Alert.find(1)
  Alert Load (1.1ms)  SELECT  "alerts".* FROM "alerts"  WHERE "alerts"."id" = $1 LIMIT 1  [["id", 1]]
=> #<Alert id: 1, title: "Et nemo magni autem similique consequuntur.", subtitle: "Aspernatur non voluptatem minus qui laudantium mol...", body: "Deserunt laboriosam quod consequuntur est dolor cu...", is_read: false, created_at: "2015-03-22 15:02:01", updated_at: "2015-03-22 15:02:01">

当我致电#to_json

时,会发生奇怪的事情
Alert.find(1).to_json
  Alert Load (4.1ms)  SELECT  "alerts".* FROM "alerts"  WHERE "alerts"."id" = $1 LIMIT 1  [["id", 1]]
=> "{\"id\":1,\"title\":\"Et nemo magni autem similique consequuntur.\",\"subtitle\":\"Aspernatur non voluptatem minus qui laudantium molestiae.\",\"body\":\"Deserunt laboriosam quod consequuntur est dolor cum molestias.\",\"is_read\":null,\"created_at\":\"2015-03-22T15:02:01.927Z\",\"updated_at\":\"2015-03-22T15:02:01.927Z\"}"

当is_read为真时,也会发生这种情况。

更多信息:

Rails 4.1.7,Ruby 2.1.2 ,Postgresql

警报

create_table "alerts", force: true do |t|
  t.string   "title"
  t.string   "subtitle"
  t.text     "body"
  t.boolean  "is_read",    default: false, null: false
  t.datetime "created_at"
  t.datetime "updated_at"
end

用于填充db的脚本:

Alert.populate 100 do |alert|
  alert.title = Faker::Lorem.sentence(3)
  alert.subtitle = Faker::Lorem.sentence(3)
  alert.body = Faker::Lorem.sentence(3)
  alert.is_read = false
end

控制器:

def show
  alert = Alert.find(params[:id])
  render json: alert
end

PostgreSQL

编辑2015年3月26日

irb(main):013:0> Alert.find(1)
  Alert Load (0.6ms)  SELECT  "alerts".* FROM "alerts"  WHERE "alerts"."id" = $1 LIMIT 1  [["id", 1]]
=> #<Alert id: 1, title: "Test", subtitle: "waaat?", body: "heeeelp", is_read: false, created_at: "2015-03-26 15:49:32", updated_at: "2015-03-26 15:56:51">
irb(main):014:0> a.body
=> "heeeelp"
irb(main):015:0> a.title
=> "Test"
irb(main):016:0> a.is_read
=> nil
irb(main):017:0> a["is_read"]
=> false
irb(main):018:0> a.update_attributes(is_read: true)
   (0.4ms)  BEGIN
  SQL (0.5ms)  UPDATE "alerts" SET "is_read" = $1, "updated_at" = $2 WHERE "alerts"."id" = 1  [["is_read", "t"], ["updated_at", "2015-03-26 15:57:26.645210"]]
   (2.1ms)  COMMIT
=> true
irb(main):019:0> a["is_read"]
=> true

最终:这是因为模型中有attr_reader:is_read。删除它将导致正确的序列化。有人可以解释一下吗?

3 个答案:

答案 0 :(得分:5)

  

最终:这是因为模型中有attr_reader:is_read。删除它将导致正确的序列化。有人可以解释一下吗?

ActiveRecord通过为每个属性创建一个getter方法,帮助将数据库字段映射到Ruby类上的方法,该方法从内部属性存储(变量)中提取值。

定义attr_reader :is_read时,实际上是:

的缩写
# app/mode/alert.rb    
def is_read
  @is_read
end

ActiveRecord::Base轻松提供的getter方法将被这个新定义的方法屏蔽。这可能是意料之外的,一开始看起来肯定是巫术。

:attr_reader的行为(有点稀疏)记录在这里:

  

创建返回每个实例变量值的实例变量和相应的方法

http://ruby-doc.org/core-1.9.3/Module.html#method-i-attr_reader

  

但为什么它会变为空?可能与渲染json:internals?

有关

这个问题分为两部分。

首先,为什么价值不存在?上面给出了答案。您的模型已经屏蔽了ActiveRecord getter方法is_read,该方法正在使用ActiveRecord的内部属性存储,其中一个指向实例变量@is_read。由于您未在模型中指定@is_read,因此会返回nil

其次,为什么null而不是nil?正如您所暗示的那样,答案与render: json有关。在JSON specification中,您有以下允许值:

  

string number object array ,true,false,null

要生成有效的JSON响应,render: json将使用JSON的nil替换Ruby的null

那么说你永远不想使用attr_readerattr_accessor等是安全的。人。在你的模特?并不是的。这些可以用作虚拟或瞬态属性,一旦对象被销毁就会丢失。但是,应该记住与ActiveRecord的交互,以避免遇到看似晦涩的错误。

答案 1 :(得分:2)

从你的编辑:

Alert < ActiveRecord::Base
  attr_reader :is_read
end

定义ActiveRecord模型时,它会自动为表中的每列创建getter和setter。这些getter和setter与使用attr_accessor,attr_reader和attr_writer创建的getter和setter不同。他们访问属性哈希而不是实例变量。使用attr_accessor,attr_reader或attr_writer将覆盖ActiveRecord自动设置的getter。如果您想要读取'is_read',请尝试以下方法:

Alert < ActiveRecord::Base
  attr_readonly :is_read
end

答案 2 :(得分:0)

因为active_record的布尔类型实际上是mysql的整数类型。

但是,您可以使用此gem来自定义您的响应json

https://github.com/rails-api/active_model_serializers