使用设计和响应_with Rails 3 中的 attr_accessible

发布于 2024-11-17 00:06:38 字数 439 浏览 1 评论 0原文

我正在使用 Rails 3 构建一个 API,使用 devise 来处理一些身份验证。

我通常使用 respond_with 方法返回各种资源的 xml/json。

例如 GET /groups.xml 将路由到

def index
  respond_with Group.all
end

This 在我的站点上对各种资源运行良好,并返回格式良好的 json 或 xml,其中包含每个组的所有属性。

但是,当我调用 GET /users.xml 时,它仅响应每个用户属性的有限子集。事实证明,这里只会返回 attr_assessible 中定义的属性 - 我怀疑这是 devise 的一个“功能”,因为任何其他模型都不是这种情况。

谁能启发我吗?

编辑:这在 Devise 1.4.2 中已得到修复。详情请参阅下文

I'm building an API with Rails 3, using devise to handle some of the authentication.

I commonly use the respond_with method to return xml/json for various resources.

Eg GET /groups.xml will route to

def index
  respond_with Group.all
end

This works fine across my site for various resources, and returns nicely formatted json or xml containing all the attributes of each group.

However, when I call GET /users.xml, it only responds with a limited subset of the each user's attributes. It turns out that only attributes defined in attr_assessible will be returned here - I suspect this is a "feature" of devise, because it's not the case for any other model.

Can anyone enlighten me?

Edit: This is sort of fixed in Devise 1.4.2. See below for details

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

影子的影子 2024-11-24 00:06:38

您的怀疑正确。 Devise Authenticatable 模块覆盖 #to_xml#to_json 首先检查类是否响应 #accessible_attributes 方法,如果响应则输出仅限于#accessible_attributes 返回的那些属性。来自authencatable.rb 的代码位于此处:

  %w(to_xml to_json).each do |method|
    class_eval <<-RUBY, __FILE__, __LINE__
      def #{method}(options={})
        if self.class.respond_to?(:accessible_attributes)
          options = { :only => self.class.accessible_attributes.to_a }.merge(options || {})
          super(options)
        else
          super
        end
      end
    RUBY
  end

您会注意到此代码将#accessible_attributes 的结果合并到任何传入的选项中。因此,您可以指定 :only 选项,例如:

.to_xml(:only => [:field, :field, :field])

这将覆盖 Devise 施加的限制并生成仅包含您指定的字段的 xml 输出。您将需要包含您想要公开的每个字段,因为一旦您使用 :only 您将胜过正常操作。

在这种情况下,我认为您无法继续在控制器中使用 respond_with 快捷方式,因为您需要直接指定 xml 输出。您可能不得不退回到老式的 respond_to 块:

respond_to do |format|
  format.xml { render :xml => @users.to_xml(:only => [:field, :field, :field]) }
  format.html
end

正如您已经发现的那样,您也可以仅添加您想要通过 attr_accessible 公开的其他字段模型类。但是,这会产生额外的副作用,使这些字段可批量分配,并且在这种情况下您可能不一定希望这样做。

Your suspicion is correct. The Devise Authenticatable module overrides #to_xml and #to_json to first check if the class responds to the #accessible_attributes method, and if it does then output is restricted to only those attributes returned by #accessible_attributes. The code from authenticatable.rb is here:

  %w(to_xml to_json).each do |method|
    class_eval <<-RUBY, __FILE__, __LINE__
      def #{method}(options={})
        if self.class.respond_to?(:accessible_attributes)
          options = { :only => self.class.accessible_attributes.to_a }.merge(options || {})
          super(options)
        else
          super
        end
      end
    RUBY
  end

You'll notice that this code merges the result of #accessible_attributes into any passed-in options. As such, you can specify an :only option, such as:

.to_xml(:only => [:field, :field, :field])

This will override the Devise-imposed restriction and produce xml output that includes only the fields you specify. You will need to include every field you want exposed, since once you use :only you'll trump the normal operation.

I don't think you'll be able to continue to use the respond_with shortcut in your controller in this case, because you'll need to specify the xml output directly. You'll probably have to fall back to an old-school respond_to block:

respond_to do |format|
  format.xml { render :xml => @users.to_xml(:only => [:field, :field, :field]) }
  format.html
end

As you already discovered, you could also just add the additional fields you want exposed via attr_accessible in the model class. However, this will have the added side-effect of making these fields mass-assignable and you may not necessarily want that in this situation.

半城柳色半声笛 2024-11-24 00:06:38

Devise 的旧版本 (<1.4.2) 对 to_json 和 to_xml 方法执行了 Monkeypatch,覆盖了 :only =>; [] 选项以及 attr_accessible 中定义的属性。恼人的。

现在这已被更改,因此serialized_hash 被覆盖,并且任何 :only =>; to_json 或 to_xml 中设置的 [:attribute] 选项将被保留。

就我而言,我最终自己对 to_json 进行了猴子修补,并向所有 ActiveRecord 模型添加了 api_accessible 方法。

class ActiveRecord::Base

  def to_json(options = {}, &block)
    if self.class.respond_to?(:api_attributes)
      super(build_serialize_options(options), &block)
    else
      super(options, &block)
    end
  end

  class << self
    attr_reader :api_attributes
    def api_accessible(*args)
      @api_attributes ||= []
      @api_attributes += args
    end
  end

  private

    def build_serialize_options(options)
      return options if self.class.api_attributes.blank?
      methods = self.class.instance_methods - self.class.attribute_names.map(&:to_sym)
      api_methods = self.class.api_attributes.select { |m| methods.include?(m) }
      api_attrs = self.class.api_attributes - api_methods
      options.merge!(only: api_attrs) if api_attrs.present?
      options.merge!(methods: api_methods) if api_methods.present?
      return options
    end

end

这意味着您现在可以定义在调用 to_json 时默认公开的属性(和方法!)列表。 Respond_with 还使用 to_json,因此它非常适合 API。

例如,user.rb

class User < ActiveRecord::Base

 devise :database_authenticatable, :registerable, :confirmable,
         :recoverable, :rememberable, :trackable, :validatable

  #Setup accessible (or protected) attributes for your model
  attr_accessible :email,
                  :password,
                  :password_confirmation,
                  :remember_me,
                  :first_name,
                  :last_name,


  api_accessible :id,
                 :name,
                 :created_at,
                 :first_name,
                 :last_name,
                 :some_model_method_also_works
end

Older versions ( < 1.4.2) of Devise performed a monkeypatch on the to_json and to_xml methods, overwriting the :only => [] option with the attributes defined in attr_accessible. Annoying.

This has now been changed, so that serializable_hash is overwritten instead, and any :only => [:attribute] options set in to_json or to_xml are persisted.

In my case, I ended up monkeypatching to_json myself, and adding a method api_accessible to all ActiveRecord models.

class ActiveRecord::Base

  def to_json(options = {}, &block)
    if self.class.respond_to?(:api_attributes)
      super(build_serialize_options(options), &block)
    else
      super(options, &block)
    end
  end

  class << self
    attr_reader :api_attributes
    def api_accessible(*args)
      @api_attributes ||= []
      @api_attributes += args
    end
  end

  private

    def build_serialize_options(options)
      return options if self.class.api_attributes.blank?
      methods = self.class.instance_methods - self.class.attribute_names.map(&:to_sym)
      api_methods = self.class.api_attributes.select { |m| methods.include?(m) }
      api_attrs = self.class.api_attributes - api_methods
      options.merge!(only: api_attrs) if api_attrs.present?
      options.merge!(methods: api_methods) if api_methods.present?
      return options
    end

end

This means that you can now define a list of attributes (and methods!) that will be exposed by default when calling to_json. Respond_with also uses to_json, so it works well for APIs.

Eg, user.rb

class User < ActiveRecord::Base

 devise :database_authenticatable, :registerable, :confirmable,
         :recoverable, :rememberable, :trackable, :validatable

  #Setup accessible (or protected) attributes for your model
  attr_accessible :email,
                  :password,
                  :password_confirmation,
                  :remember_me,
                  :first_name,
                  :last_name,


  api_accessible :id,
                 :name,
                 :created_at,
                 :first_name,
                 :last_name,
                 :some_model_method_also_works
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文