Rails REST API-基于模型';为json输入生成允许的字段;没有硬编码属性的嵌套关联

Rails REST API-基于模型';为json输入生成允许的字段;没有硬编码属性的嵌套关联,json,ruby,rest,ruby-on-rails-5,Json,Ruby,Rest,Ruby On Rails 5,我正在为应用程序的工厂生成器开发一个RESTAPI,以允许也使用REST创建模型实例,我想知道如何在不硬编码属性的情况下允许模型的嵌套属性 假设我有一个叫餐馆的模型 class Restaurant < ApplicationRecord has_one :cost, dependent: :destroy, inverse_of: :grant has_one :address, dependent: :destroy, inverse_of: :grant has_one

我正在为应用程序的工厂生成器开发一个RESTAPI,以允许也使用REST创建模型实例,我想知道如何在不硬编码属性的情况下允许模型的嵌套属性

假设我有一个叫餐馆的模型

class Restaurant < ApplicationRecord
  has_one :cost, dependent: :destroy, inverse_of: :grant
  has_one :address, dependent: :destroy, inverse_of: :grant
  has_one :owner, dependent: :destroy, inverse_of: :grant

  accepts_nested_attributes_for :cost, :address, :owner
  ...
其中嵌套的关联也有自己的工厂。我以这种格式通过RESTAPI传递json正文

{
  "restaurant": {
    "cost_attributes": {
     "staff": 123
     "tables": 123
    }
}
我知道我可以允许这些属性

params.permit(:restaurant)
      .permit(cost_attributes: [:staff, :tables],
              address_attributes: [:address_attr_1],
              owner_attributes: [:owner_attr_1]]
但我正在研究的实际模型有很多联系。硬编码每件事都会很痛苦有没有办法允许参数通过而不是在控制器中硬编码?目前我就是这么想的

params.permit(Restaurant.nested_attributes_options.keys)

但显然这不起作用。

这里有一种收集嵌套属性名称的方法:

mod = Restaurant

nested_attributes = mod.nested_attributes_options.keys.each_with_object({}) do |association, hash|
  hash[:"#{association}_attributes"] = association.
                                       to_s.
                                       classify.
                                       constantize.
                                       send(:attribute_names).
                                       map(&:to_sym)
end
params.permit(:restaurant).permit(nested_attributes)

这段代码有点通用,请毫不犹豫地根据您的特定用例对其进行调整。

为了改进您的答案,以下是一种使用反射递归收集所有深度嵌套属性名称的方法:

  def get_nested_attributes(model, _attributes = [])

    # base case
    if model.nested_attributes_options.empty?
      nil

    # recurse deeper
    else
      associations_list = model.reflect_on_all_associations(:has_one)
      associations_list = associations_list.push(model.reflect_on_all_associations(:has_many)).flatten

      nested_attributes = associations_list.each_with_object({}) do |association , hash|

        association_model = association.klass.nil? ? association.name.to_s.classify.constantize : association.klass

        # include attributes of association model
        # { _attributes => [attributes_1, attribute_2] }
        attribute_name = :"#{association.name.to_s}_attributes"
        hash[attribute_name] = association_model
                               .send(:attribute_names)
                               .map(&:to_sym)

        # recurse deeper into tree, get the result and append
        # { _attributes => [attributes_1, attribute_2, { } ] }
        result = get_nested_attributes(association_model, hash[attribute_name])
        if !result.nil?
          hash[attribute_name].push(result)
        end
      end
      nested_attributes
    end
  end

model = Restaurant
nested_attributes = get_nested_attributes(model)
params.permit(nested_attributes)
  def get_nested_attributes(model, _attributes = [])

    # base case
    if model.nested_attributes_options.empty?
      nil

    # recurse deeper
    else
      associations_list = model.reflect_on_all_associations(:has_one)
      associations_list = associations_list.push(model.reflect_on_all_associations(:has_many)).flatten

      nested_attributes = associations_list.each_with_object({}) do |association , hash|

        association_model = association.klass.nil? ? association.name.to_s.classify.constantize : association.klass

        # include attributes of association model
        # { _attributes => [attributes_1, attribute_2] }
        attribute_name = :"#{association.name.to_s}_attributes"
        hash[attribute_name] = association_model
                               .send(:attribute_names)
                               .map(&:to_sym)

        # recurse deeper into tree, get the result and append
        # { _attributes => [attributes_1, attribute_2, { } ] }
        result = get_nested_attributes(association_model, hash[attribute_name])
        if !result.nil?
          hash[attribute_name].push(result)
        end
      end
      nested_attributes
    end
  end

model = Restaurant
nested_attributes = get_nested_attributes(model)
params.permit(nested_attributes)