【问题标题】:Create an ActiveRecord Instance from Json从 Json 创建一个 ActiveRecord 实例
【发布时间】:2013-05-10 17:16:40
【问题描述】:

在不触发任何数据库操作的情况下创建新的 Ruby/Rails 对象的最佳方法是什么?

例如,如果我有一个任务类

class Task < ActiveRecord::Base
  has_many :tags
  has_one :location

end

我想从缓存数据中创建一个看起来像这样的数据

task_json = { 
  id: 1, 
  title: 'My Task', 
  tags: [ 
    { title: 'Tag1' }, 
    { title: 'Tag2'} 
  ]
  location: { lat: 46.0, lon: -120.1 } 
} 

我想重构对象,但不触发任何数据库交互。

@task = Task.new
@task.id = task_json['id']
@task.title = task_json['title']
@task.location = Location.new(:lat => task_json['location']['lat'], :lon => task_json['location']['lon'])

@task.tags = ...

理想情况下,一旦构建了对象,就不会以任何方式进行保存或数据库交互。

【问题讨论】:

    标签: ruby-on-rails caching activerecord


    【解决方案1】:

    您可以尝试以这种方式在您的 JSON 字符串 task_json 上使用 JSON.parse

    task = Task.new(JSON.parse(task_json))
    

    【讨论】:

    • json 响应不是对象的精确表示,所以这不能完美地工作,但我可以做一些。
    • 好吧,如果它不是一个精确的表示,恐怕你将不得不手动构建你的对象。除非有另一种我不知道的方式?
    • 有了这个我得到一个ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes: created_at, id... 我该如何解决这个问题?
    • 您可以将第二个参数传递给 .new task = Task.new(JSON.parse(task_json), without_protection: true)
    • 参见stackoverflow.com/questions/7879637/… 了解可用于解决protected_attributes 问题的自定义from_json 方法
    【解决方案2】:

    在不触发任何数据库操作的情况下创建新的 Ruby/Rails 对象的最佳方法是什么?

    例如,如果我有一个任务类

    class Task &lt; ActiveRecord::Base

    普通的旧 Ruby 对象

    如果你想要一个不持久的 PORO(普通的旧 Ruby 对象),为什么还要子类化 ActiveRecord::Base?您可以将其放在任何可自动加载的路径中,例如 app/models/task.rb:

    class Task
      attr_reader :tags, :location, ...
      def initialize(...)
        @tags = ...
        @location = ...
        ...
      end
    end
    

    然后在其他地方创建它:

    task = Task.new(...)
    

    然后您可以访问它的部分,例如:

    task.tags
    

    您可以在哪里传入解析的 JSON、JSON 字符串、args 等。

    但是,来自JSON.parse(some_json_string) 的哈希本身是一个 Ruby 对象,因此您可以轻松地使用它。它也不会触发任何数据库操作。 :) 你可能使用像 Task 这样的类的主要原因是让它更清楚它是什么以及对它的期望,但是哈希也很容易使用。只是取决于。

    如果你真的想要一个只读的 ActiveRecord 模型,你可以使用几个 gem 之一。我写了一个其他人贡献的名为activerecord-be_readonly,但是你需要这样的东西的时间应该很少,而且它更适合从数据库读取数据然后尝试不允许数据库通过该模型更新该特定表。

    仅在从 JSON 创建时将 ActiveRecord 模型实例设为只读

    在您的评论中,您说如果我理解正确,您只想将其从缓存中提取为只读。

    在此处查看 activerecord-be_readonly gem 如何改变 AR 行为:https://github.com/garysweaver/activerecord-be_readonly/blob/master/lib/activerecord-be_readonly/model.rb

    我猜你会想要在使用 JSON 哈希初始化模型时设置一个标志,类似的方法可以查看该标志,并在设置该标志时引发 ActiveRecord::ReadOnlyRecord 或类似的。

    这只会保护您使用这些方法的模型。如果您与其他模型有关联,除非它们使用类似的方法,否则它不会保护它们。最后,如果您绝对必须这样做,那么使用 PORO 和常规 AR 模型可能会更好。

    但是,这听起来使用起来会很痛苦。我会避免任何让用户(或开发人员)摸不着头脑的事情。

    【讨论】:

    • 我只希望它在我从缓存中加载 json 时以这种方式运行。否则,我想要所有 ActiveRecord 功能。我们有很多时候从 MySql 读取/写入,但为了提高搜索性能,列表项响应缓存在 json 中(对于 api 和 web)。我想使用 AR 类,因此视图中的“render @task”可以与对象 AR 或 PORO 一起使用。我想我可以复制所有的类并伪装它们进行渲染,但我宁愿不这样做。
    • 另一件快速的事情,您说过“但是为了提高搜索性能,列表项响应缓存在 json 中”。 AR 已经缓存,如果您遇到性能问题,您是在进行 n+1 查询还是使用include:、索引、额外的数据库优化、在网络服务器级别缓存响应、差异服务器/服务器优化、减少来自的调用客户端等?
    • 我们的流量很大。列表中的项目有很多子/嵌套对象。即使包含子对象,缓存结果也很有帮助。我们目前将 html 缓存在 memcache 中,但我对缓存 json 感兴趣,以便 api(iphone、android、windows)以及 web 应用程序使用它。
    • 我想我找到了一种避免触发数据库操作的方法 - 通过调用 task[:location] = Location.new 和 task[:tags]
    • 如果是我,我会将提供读写和只读数据的服务/操作分开。同时使用两者可能会使其他开发人员感到困惑,并且更有可能产生支持问题。顺便说一句-我是贵公司/网站的忠实粉丝。定期使用它。很棒的信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-30
    • 2013-09-13
    相关资源
    最近更新 更多