Rails Object เพื่อแฮช


147

ฉันมีวัตถุต่อไปนี้ที่ถูกสร้างขึ้น

@post = Post.create(:name => 'test', :post_number => 20, :active => true)

เมื่อสิ่งนี้ถูกบันทึกไว้ฉันต้องการที่จะได้รับวัตถุกลับไปที่แฮชเช่นโดยการทำบางสิ่งเช่น:

@object.to_hash

สิ่งนี้เป็นไปได้อย่างไรจากภายในราง

คำตอบ:


296

หากคุณกำลังมองหาคุณลักษณะเฉพาะจากนั้นคุณสามารถรับได้โดย:

@post.attributes

30
อย่าใช้เมื่อบ่วงนี้วิธีการที่มีราคาแพง ไปกับ as_json
AnkitG

5
.to_jsonจะสืบค้น DB ถ้าแบบจำลองไม่สมบูรณ์
ykhrustalev

1
ทำงานร่วมกับjoinsและselect, Person.joins(:address).select("addresses.street, persons.name").find_by_id(id).attributesจะกลับ { street: "", name: "" }
Fangxing

3
@AnkitG ฉันไม่เชื่อว่า as_json มีราคาถูกลง หากคุณดูซอร์สโค้ดas_jsonมันจะเรียกserializable_hashว่าอะไรในที่สุดก็จะเรียกattributes! ดังนั้นคำแนะนำของคุณคือการเพิ่มความซับซ้อนสองชั้นจริง ๆattributesทำให้มันมีราคาแพงกว่า
sandre89

2
.as_jsonจะทำให้วัตถุเป็นอนุกรมเพื่อแฮช ruby
roadev

45

ใน Rails รุ่นล่าสุด (ไม่สามารถบอกได้ว่าแบบใด) คุณสามารถใช้as_jsonวิธีนี้ได้:

@post = Post.first
hash = @post.as_json
puts hash.pretty_inspect

จะส่งออก:

{ 
  :name => "test",
  :post_number => 20,
  :active => true
}

หากต้องการเพิ่มเติมอีกเล็กน้อยคุณสามารถแทนที่วิธีดังกล่าวเพื่อกำหนดลักษณะที่ปรากฏของแอตทริบิวต์ด้วยการทำสิ่งนี้:

class Post < ActiveRecord::Base
  def as_json(*args)
    {
      :name => "My name is '#{self.name}'",
      :post_number => "Post ##{self.post_number}",
    }
  end
end

จากนั้นด้วยอินสแตนซ์เดียวกันกับด้านบนจะแสดงผล:

{ 
  :name => "My name is 'test'",
  :post_number => "Post #20"
}

หลักสูตรนี้หมายความว่าคุณต้องระบุอย่างชัดเจนว่าจะต้องแสดงแอตทริบิวต์ใด

หวังว่านี่จะช่วยได้

แก้ไข:

นอกจากนี้คุณสามารถตรวจสอบอัญมณีHashifiable


OP ไม่ขอ JSON แต่สำหรับแฮช
David Hempy

5
@DavidHempy โปรดอ่านคำตอบของฉันอย่างละเอียดก่อน downvoting ดังแสดงในตัวอย่างของฉันของฉันนี้เป็นสิ่งที่ไม่ #as_json และมีไว้สำหรับ: api.rubyonrails.org/classes/ActiveModel/Serializers/... ฉันไม่ได้เลือกชื่อของวิธีการนั้น
Raf

25
@object.as_json

as_json มีวิธีที่ยืดหยุ่นในการกำหนดค่าวัตถุที่ซับซ้อนตามความสัมพันธ์ของแบบจำลอง

ตัวอย่าง

แคมเปญรุ่นเป็นของช็อปและมีหนึ่งรายการ

รายการแบบจำลองมีlist_tasksจำนวนมากและแต่ละรายการlist_tasksมีความคิดเห็นมากมาย

เราสามารถรับ json หนึ่งอันซึ่งรวมข้อมูลเหล่านั้นทั้งหมดได้อย่างง่ายดาย

@campaign.as_json(
    {
        except: [:created_at, :updated_at],
        include: {
            shop: {
                except: [:created_at, :updated_at, :customer_id],
                include: {customer: {except: [:created_at, :updated_at]}}},
            list: {
                except: [:created_at, :updated_at, :observation_id],
                include: {
                    list_tasks: {
                        except: [:created_at, :updated_at],
                        include: {comments: {except: [:created_at, :updated_at]}}
                    }
                }
            },
        },
        methods: :tags
    })

วิธีแจ้งให้ทราบ:: แท็กสามารถช่วยคุณแนบวัตถุเพิ่มเติมใด ๆ ที่ไม่มีความสัมพันธ์กับผู้อื่น คุณเพียงแค่ต้องกำหนดวิธีการที่มีชื่อแท็กในรูปแบบแคมเปญ วิธีนี้ควรคืนสิ่งที่คุณต้องการ (เช่น Tags.all)

เอกสารอย่างเป็นทางการสำหรับas_json


ทำฟังก์ชั่นที่กำหนดเองก่อนที่จะพบสิ่งนี้ ต้องการวิธีการแบบใช้ครั้งเดียวมากกว่าการกำหนดฟังก์ชั่นสำหรับชั้นเรียน พลาดสิ่งนี้แม้หลังจากทำงานกับวิธีการทำให้เป็นอันดับ XML ด้วยเหตุผลบางประการ to_ตัวแปรที่ดูเหมือนว่าจะทำงานเกือบตรงเช่นเดียวกับas_ตัวแปรยกเว้นสำหรับการส่งออกที่ยกมา สิ่งเดียวที่ฉันไม่ชอบคือไม่รักษาลำดับตัวกรองของฉัน ดูเหมือนว่าจะเรียงตามตัวอักษร ฉันคิดว่ามันเกี่ยวข้องกับอัญมณี Awesome_print ที่ฉันมีในสภาพแวดล้อมของฉัน แต่ฉันไม่คิดว่าเป็นเช่นนั้น
Pysis

8

คุณสามารถรับคุณลักษณะของวัตถุต้นแบบที่ส่งคืนเป็นแฮชโดยใช้

@post.attributes

หรือ

@post.as_json

as_jsonอนุญาตให้คุณรวมการเชื่อมโยงและแอททริบิวรวมถึงการระบุแอททริบิวที่จะรวม / แยก (ดูเอกสารประกอบ ) แต่ถ้าคุณต้องการเพียงคุณสมบัติของวัตถุฐานเปรียบเทียบใน app ของฉันกับทับทิม 2.2.3 และ 4.2.2 รางแสดงให้เห็นว่าต้องมีน้อยกว่าครึ่งหนึ่งเป็นเวลามากattributesas_json

>> p = Problem.last
 Problem Load (0.5ms)  SELECT  "problems".* FROM "problems"  ORDER BY "problems"."id" DESC LIMIT 1
=> #<Problem id: 137, enabled: true, created_at: "2016-02-19 11:20:28", updated_at: "2016-02-26 07:47:34"> 
>>
>> p.attributes
=> {"id"=>137, "enabled"=>true, "created_at"=>Fri, 19 Feb 2016 11:20:28 UTC +00:00, "updated_at"=>Fri, 26 Feb 2016 07:47:34 UTC +00:00}
>>
>> p.as_json
=> {"id"=>137, "enabled"=>true, "created_at"=>Fri, 19 Feb 2016 11:20:28 UTC +00:00, "updated_at"=>Fri, 26 Feb 2016 07:47:34 UTC +00:00}
>>
>> n = 1000000
>> Benchmark.bmbm do |x|
?>   x.report("attributes") { n.times { p.attributes } }
?>   x.report("as_json")    { n.times { p.as_json } }
>> end
Rehearsal ----------------------------------------------
attributes   6.910000   0.020000   6.930000 (  7.078699)
as_json     14.810000   0.160000  14.970000 ( 15.253316)
------------------------------------ total: 21.900000sec

             user     system      total        real
attributes   6.820000   0.010000   6.830000 (  7.004783)
as_json     14.990000   0.050000  15.040000 ( 15.352894)

as_json จะโทรสอบถามฐานข้อมูลอีกครั้งถ้าคุณกำลังเรียกคืนทรัพยากรที่ซ้ำซ้อนด้วยการเข้าร่วม metho
โทนี่ Hsieh

6

มีคำแนะนำที่ดีที่นี่

ฉันคิดว่ามันคุ้มค่าที่จะสังเกตเห็นว่าคุณสามารถใช้งานรูปแบบ ActiveRecord เป็นแฮชได้:

@customer = Customer.new( name: "John Jacob" )
@customer.name    # => "John Jacob"
@customer[:name]  # => "John Jacob"
@customer['name'] # => "John Jacob"

ดังนั้นแทนที่จะสร้างแฮชของแอททริบิวต์คุณสามารถใช้วัตถุนั้นเป็นแฮชได้


6

คุณสามารถใช้แอตทริบิวต์เพื่อส่งคืนแอตทริบิวต์ทั้งหมด แต่คุณสามารถเพิ่มวิธีการโพสต์เพื่อโพสต์เรียกมันว่า "to_hash" และให้คืนค่าข้อมูลที่คุณต้องการในแฮช สิ่งที่ต้องการ

def to_hash
 { name: self.name, active: true }
end

2

ไม่แน่ใจว่าเป็นสิ่งที่คุณต้องการหรือไม่ลองที่ ruby ​​console:

h = Hash.new
h["name"] = "test"
h["post_number"] = 20
h["active"] = true
h

เห็นได้ชัดว่ามันจะทำให้คุณแฮชในคอนโซล หากคุณต้องการคืนค่าแฮชจากภายในเมธอด - แทนที่จะใช้เพียงแค่ "h" ลองใช้ "return h.inspect" สิ่งที่คล้ายกับ:

def wordcount(str)
  h = Hash.new()
  str.split.each do |key|
    if h[key] == nil
      h[key] = 1
    else
      h[key] = h[key] + 1
    end
  end
  return h.inspect
end

ผู้โพสต์ถามถึงรุ่นของ ActiveRecord ใน Rails
Jeffrey Harrington


0

คำตอบของ Swanand นั้นยอดเยี่ยม

ถ้าคุณกำลังใช้ FactoryGirl คุณสามารถใช้มันวิธีการในการสร้างกัญชาแอตทริบิวต์โดยไม่ต้องคีย์build idเช่น

build(:post).attributes

0

คำถามเก่า แต่มีการอ้างอิงอย่างมาก ... ฉันคิดว่าคนส่วนใหญ่ใช้วิธีการอื่น แต่มีto_hashวิธีการinfact ก็ต้องมีการติดตั้ง โดยทั่วไปการถอนเป็นคำตอบที่ดีกว่าหลังจากทางรถไฟ 4 ... ตอบคำถามนี้ส่วนใหญ่เพราะฉันต้องค้นหากลุ่มเพื่อค้นหาหัวข้อนี้หรือสิ่งที่มีประโยชน์ & สมมติว่าคนอื่น ๆ กำลังตีปัญหาเดียวกัน ...

หมายเหตุ: ไม่แนะนำสิ่งนี้สำหรับทุกคน แต่เป็นกรณีขอบ!


จากทับทิมบนราง API ... http://api.rubyonrails.org/classes/ActiveRecord/Result.html ...

This class encapsulates a result returned from calling #exec_query on any database connection adapter. For example:

result = ActiveRecord::Base.connection.exec_query('SELECT id, title, body FROM posts')
result # => #<ActiveRecord::Result:0xdeadbeef>

...

# Get an array of hashes representing the result (column => value):
result.to_hash
# => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
      {"id" => 2, "title" => "title_2", "body" => "body_2"},
      ...
     ] ...
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.