Как принять JSON через Rails API без «_трибутов» для вложенных полей

18

Я написал API с Rails и должен принимать некоторые вложенные_трибуты в вызовах API.

В настоящее время я отправляю данные через

PATCH /api/v1/vintages/1.json

{
  "vintage": {
    "year": 2014,
    "name": "Some name",
    "foo": "bar",
    "sizes_attributes": [
      {
        "name": "large",
        "quantity": "12"
      },
      {
        "name": "medium",
        "quantity": "2"
      }
    ]
  }
}

Однако я хотел бы выполнить следующее:

PATCH /api/v1/vintages/1.json

{
  "vintage": {
    "year": 2014,
    "name": "Some name",
    "foo": "bar",
    "sizes": [
      {
        "name": "large",
        "quantity": "12"
      },
      {
        "name": "medium",
        "quantity": "2"
      }
    ]
  }
}

Разница заключается в атрибутах , являющихся частью ключа полей. Я хочу иметь возможность accept_nested_attributes_for :sizes без использования _attributes быть частью объекта JSON.

Кто-нибудь знает, как это сделать?

    
задан Brian Weinreich 03.12.2014 в 00:09
источник
  • ну, это было бы хакерским, но не могло просто сделать что-то params [: vintage] [: sizes_attributes] = params [: vintage] [: sizes], чтобы переименовать их. –  Doon 05.12.2014 в 20:28

2 ответа

22

Вы можете использовать магию в своих сильных параметрах. На основе того, что вы хотите, вы, вероятно, имеете этот метод в своем контроллере:

def vintage_params
  params.require(:vintage).permit(:year, :name, :foo, { sizes: [:name, :quantity] })
end

Все, что вам нужно сделать, это отредактировать имя sizes в этом методе. Я бы рекомендовал:

def vintage_params
  vintage_params = params.require(:vintage).permit(:year, :name, :foo, { sizes: [:name, :quantity] })
  vintage_params[:sizes_attributes] = vintage_params.delete :sizes
  vintage_params.permit!
end

Это приведет к удалению ключа :sizes и добавит его в ожидаемый :sizes_attributes , не испортив ваш симпатичный json. Вы ничего не можете сделать напрямую с accepts_nested_attributes_for , чтобы изменить имя.

    
ответ дан ptd 05.12.2014 в 20:56
источник
  • «Вы ничего не можете сделать непосредственно с accepts_nested_attributes_for для изменения имени». Это правильно и несчастливо. У меня была такая же проблема. –  lightswitch05 10.12.2014 в 16:33
  • Это не работает для меня. Парамы переименовываются правильно, но когда я пытаюсь выполнить Model.new (params), я получаю ActiveModel :: ForbiddenAttributesError. По-видимому, перед назначением атрибутов экземпляру модели есть дополнительная проверка. –  jmarceli 08.01.2016 в 16:14
  • Возможно, вам понадобится сделать vintage_params.permit! как последняя строка в Rails 4.2 и более поздних версиях. –  ptd 08.01.2016 в 21:55
5

Я тоже искал способ избежать загрязнения моего RESTful API вложенными атрибутами cruft. Я думал, что поделюсь своим решением, поскольку он достаточно общий, чтобы быть полезным для тех, кто работает в одной и той же проблеме. Он начинается с простого модуля, который можно использовать с вашего контроллера:

module PrettyApi
  class << self
    def with_nested_attributes(params, attrs)
      return if params.blank?

      case attrs
      when Hash
        with_nested_hash_attributes(params, attrs)
      when Array
        with_nested_array_attributes(params, attrs)
      when String, Symbol
        unless params[attrs].blank?
          params["#{attrs}_attributes"] = params.delete attrs
        end
      end
      params
    end

    private

    def with_nested_hash_attributes(params, attrs)
      attrs.each do |k, v|
        with_nested_attributes params[k], v
        with_nested_attributes params, k
      end
    end

    def with_nested_array_attributes(params, attrs)
      params.each do |np|
        attrs.each do |v|
          with_nested_attributes np, v
        end
      end
    end
  end
end

Вот пример использования этого модуля в контроллере, который используется для загрузки адресных книг с мобильного клиента:

class V1::AddressBooksController < V1::BaseController
  def create
    @address_book = AddressBook.new address_book_params
    unless @address_book.save
      errors = @address_book.errors.to_hash(true)
      render status: 422, json: { errors: errors }
    end
  end

  private

  def address_book_params
    PrettyApi.with_nested_attributes  pretty_address_book_params,
                                      contacts: [:emails, :phones, :addresses]
  end

  def pretty_address_book_params
    params.permit(
      :device_install_id,
      contacts: [
        :local_id,
        :first_name,
        :last_name,
        :nickname,
        emails: [
          :value,
          :type
        ],
        phones: [
          :value,
          :type
        ],
        addresses: [
          :type,
          :street_address,
          :city,
          :state,
          :postal_code,
          :country
        ]
      ]
    )
  end
end

Примечание. Синтаксис объявления вложенных атрибутов отражает то, что объявление допустимых параметров в вашем контроллере.

Вот пример Gist для этого примера.

Я надеюсь, что кто-то найдет это полезным!

    
ответ дан Tim Lowrimore 06.07.2015 в 06:08
источник
  • Работал для меня, спасибо. Хотелось бы, чтобы вы ввели некоторые инструкции, поскольку это не было самоочевидным, как это сработало. –  Snowman 28.11.2016 в 00:52