Слияние Rails (и ручное назначение) с нулевым назначением

Я пытаюсь создать модель в контроллере, используя сильные параметры в Rails 5.1 (некоторые вещи изменились по сравнению с предыдущими для strong_params). Однако, когда я проверяю параметры, объединенных НЕТ, и я получаю ForbiddenAttributesError, отслеживающий строку Model.new ниже. Единственное, что в модели, это проверить наличие всех атрибутов.

class ModelController < ApplicationController
  before_action :application_controller_action

  def create
    @model = Model.new(strong_params)
    if @model.valid?
      result = @model.save
    else
      render html: 'MODEL NOT VALID'
    end
    render html: 'DONE'
  end

  private
    def strong_params
      # attr_1 and attr_2 are set in the application controller and are available here.
      params.require(:model).permit(:name, :attribute_1, :attribute_2).merge(attribute_1: @attr_1, attribute_2: @attr_2)
      # Inserting the following two lines causes a ForbiddenAttributesError
      puts params.inspect  # DOES NOT INCLUDE @attr_1 and/or @attr_2
      return params
    end

Возможно, я делаю что-то не так, потому что я даже пытался поместить сильные параметры в модель с атрибутами (которые я могу проверить непосредственно перед этим), и это все равно не удается, потому что проверка attr_1 и attr_2 не выполняется в модели.

def create
  puts @user.inspect  (not nil)
  @model = Model.new(name: strong_params[:name], attribute_1: @attr_1, attribute_2: @attr_2)

ОБНОВИТЬ:

Хорошо, я получаю странные ошибки при устранении неполадок. Кажется, слияние работает неправильно, хотя я уверен, что когда-то так и было.

Первое, что я проверил, это @attr_1 и @attr_2, они определенно устанавливаются.

В целях устранения неполадок я сократил приложение before_action до этого:

def application_before_action
  @attr_1 = Model.first
  @attr_2 = Model.last

С приведенным выше кодом, проверяя объект params, а затем возвращая его после require().permit(), я получаю ForbiddenAttributesError (без указания на что). Если я удалю эти строки, я получу сообщение об ошибке отсутствующих атрибутов модели, указывающее, что @attr_1 и @attr_2 отсутствуют.

ОБНОВЛЕНИЕ 2

Изменил название вопроса, так как наверное запутался при поиске неисправности. Я думаю, что проблема в том, что слияние присваивает nil... но, как ни странно, ручное назначение, предложенное (первоначально мной), и другой ответ здесь. Ключи атрибутов есть, но им присваивается нуль. Кроме того, заметил, что в моем примере использовалась одна модель, хотя на самом деле есть две модели: Model1 и Model2. Я присваиваю значения из Model1 в Model2.

Вот лучшая демонстрация ошибки:

def create
    puts '0:'
    puts @model1.inspect
    puts '1:'
    puts strong_params.inspect
    @model2 = Model2.new(strong_params) do |m|
      m.user_id = @attr_1
      m.account_number = @attr_2
    end
    puts '3:'
    puts @model2.inspect

    if @model2.valid?
      result = @model2.save
      render html: 'SUCCESS' and return
    else
      render html: @model2.errors.full_messages and return
    end
end

Выводы в консоли:

0:
#<Model1 id: 29, attribute_1: 'test_value_1', attribute_2: 'test_value_2', created_at: "2018-08-15 03:55:08", updated_at: "2018-08-15 04:05:01">
1:
<ActionController::Parameters {"name"=>"test_name", "attribute_1"=>nil, "attribute_2"=>nil} permitted: true>
3:
#<Model2 id: nil, name: 'test_name', attribute_1: nil, attribute_2: nil, created_at: nil, updated_at: nil>

Очевидно, что нулевой идентификатор и метки времени связаны с тем, что модель еще не сохранена.

HTML model2.errors.full_messages: ["attribute_1 не может быть пустым", "attribute_2 не может быть пустым"]

РЕШЕНИЕ

Раньше, когда я работал в чисто рубиновой среде, я ошибался насчет методов доступа ActiveRecord по умолчанию для моделей. Удаление аксессоров, похоже, решило проблему.


person user58446    schedule 14.08.2018    source источник
comment
Откуда взялись @attr_1 и @attr_2? Где :application_controller_action защита?   -  person chumakoff    schedule 14.08.2018
comment
strong_params — это обычный метод. Он не будет выполнен, пока вы не вызовете strong_params в своем коде. Я думаю, проблема в том, что application_controller_action никогда не выполнялся (или не устанавливал @attr_1 и @attr_2). Вы обдумывали это?   -  person MrShemek    schedule 14.08.2018
comment
@ user58446 Я предполагаю, что вы получаете name в контроллере, но как вы получаете @attr_1 и @attr_2. Являются ли они также частью запроса?   -  person MrShemek    schedule 14.08.2018
comment
Извините все, был занят устранением неполадок. Пожалуйста, смотрите обновление. attr_1 и attr_2 не являются частью запроса, они устанавливаются в before_action (в контроллере приложения) и «предназначены» для добавления в хэш params. Странно, он даже не позволяет мне создать объект вручную, не используя только хэш. Остановит ли это отключение сильных параметров?   -  person user58446    schedule 14.08.2018
comment
Кроме того, мне кажется странным, что я должен что-то разрешать, даже если я это объединяю. Я думаю, что Rails будет различать параметры интерфейса и параметры сервера, если это имеет смысл.   -  person user58446    schedule 14.08.2018
comment
@user58446 user58446 Если вы переопределяете атрибуты через секунду после их разрешения, нет причин разрешать их вообще. Я бы оставил их. Кроме того, вы говорите, что @attr_1 и @attr_2 определенно устанавливаются. Однако это не означает, что они не могут быть nil. Даже Model.first и Model.last могут возвращать nil, если записей нет. Вы проверили значения @attr_1 и @attr_2 перед назначением?   -  person 3limin4t0r    schedule 15.08.2018


Ответы (2)


Вместо того, чтобы возиться с хешем params, вы можете просто присвоить нечетные значения одно за другим:

class ModelController < ApplicationController
  before_action :application_controller_action

  def create
    @model = Model.new(strong_params) do |m|
      m.attribute_1 = @attr_1
      m.attribute_2 = @attr_2
    end

    if @model.valid?
      result = @model.save
    else
      render html: 'MODEL NOT VALID'
    end
    # don't do this it will just give a double render error
    render html: 'DONE'
  end

  private

   private
    def strong_params
      params.require(:model).permit(:name, :attribute_1, :attribute_2)
    end
end

В общем, это гораздо более читаемый способ, например, объединить параметры со значениями из сеанса.

Причина, по которой ваш метод сильных параметров не работает, заключается в том, что он просто сломан всеми возможными способами. Суть в том, что вы не возвращаете хэш параметров из белого списка и объединенных параметров. Ты возвращаешь весь этот хлам.

У вас также создается ложное впечатление, что .require, .permit и .merge изменяют исходный хеш - они не изменяют - они возвращают новый хеш (ну, на самом деле, экземпляр ActionContoller:: Parameters, если быть точным).

def strong_params
  # attr_1 and attr_2 are set in the application controller and are available here.
  permitted = params.require(:model).permit(:name, :attribute_1, :attribute_2)
        .merge(attribute_1: @attr_1, attribute_2: @attr_2)
  puts permitted.inspect
  permitted # return is implicit
end

Или просто:

def strong_params
  # attr_1 and attr_2 are set in the application controller and are available here.
  params.require(:model).permit(:name, :attribute_1, :attribute_2)
        .merge(attribute_1: @attr_1, attribute_2: @attr_2)
end
person max    schedule 14.08.2018
comment
Спасибо за вашу честность: причина, по которой ваш метод сильных параметров не работает, заключается в том, что он просто сломан всеми возможными способами. Я думал, что просто следую приведенному здесь примеру плюс слияние: edgeapi.rubyonrails.org/classes /ActionController/. В любом случае, ваш пример с блоком не работает. Я все еще действую? ошибки: [атрибут_1 не может быть пустым, атрибут_2 не может быть пустым]. Я могу проверить @attrs прямо перед их использованием, и они определенно установлены. И спасибо, что указали на ошибку, связанную с тем, что я не присвоил значение из require().permit(). - person user58446; 15.08.2018
comment
Кроме того, у меня есть attr_accessors, установленный для всех задействованных атрибутов... запись для модели attribute_1 и attribute_2 и чтение для другого. - person user58446; 15.08.2018
comment
См. последнее обновление с лучшей демонстрацией проблемы. Это, вероятно, что-то простое, но я не уверен, где исследовать отсюда. - person user58446; 15.08.2018
comment
@ user58446: но я не знаю, что делать дальше - исследуйте фактические значения @attr_1/@attr_2. Также они кажутся экземплярами модели, а не простыми примитивами? В этом случае попробуйте сохранить их следующим образом: m.attr1_id = @attr_1.id. Должен делать то же самое, если все хорошо, но произойдет сбой, если этот объект равен нулю. - person Sergio Tulentsev; 15.08.2018
comment
Вы не хотите использовать attr_accessor для атрибутов, поддерживаемых базой данных, так как это перезапишет установщики/геттеры, созданные ActiveRecord. Я немного не понимаю, куда идти дальше, потому что у вас просто настоящий беспорядок, и я действительно не понимаю, чего вы пытаетесь достичь. - person max; 15.08.2018
comment
@max: да, и поддельные имена атрибутов не помогают. - person Sergio Tulentsev; 15.08.2018
comment
@max Ну вот и все. Я удалил attr_accessors и все заработало, но теперь я еще больше запутался. Я новичок в rails 2.5.1, поэтому я не уверен, что мне чего-то не хватает, но я добавил средства доступа для устранения предыдущей ошибки. Кроме того, кажется, есть важная информация, предполагающая, что вам нужно написать их, чтобы читать/писать в них. Наконец, я написал правильные средства доступа для правильных атрибутов, поэтому они должны были иметь возможность читать и записывать соответствующие атрибуты. Могу ли я просмотреть в приложении средства доступа по умолчанию? Это рубин против рельсов?... - person user58446; 15.08.2018
comment
Вот это: stackoverflow.com/ вопросов/1588803/, но это не полностью объясняет, почему у меня не получалось, если только в средствах доступа ActiveRecord по умолчанию не происходит волшебства (вероятно, так). - person user58446; 15.08.2018
comment
attr_accessor — обычный рубин. Это метод метапрограммирования, который создает методы установки и получения. ActiveRecord делает нечто подобное на основе схемы базы данных. Я бы посоветовал вам сделать несколько базовых руководств по ruby, таких как tryruby.org, так как это довольно простые вещи. - person max; 15.08.2018
comment
Это нормально. Я сделал больше, чем несколько руководств, но это, должно быть, упустили из виду. Я убежден, что под капотом Rails есть вещи, которые я никогда не пойму. Однако, если все, что у меня есть, это объект, который еще не сохранен в БД, я бы ожидал, что сработает обычный геттер и сеттер или значение по умолчанию, предоставляемое рубиновым attr_reader/attr_writer. - person user58446; 15.08.2018
comment
Оно делает. Но я бы посоветовал вам задать отдельный вопрос, на который действительно можно ответить, вместо того, чтобы делать кучу предположений. - person max; 15.08.2018

Вы можете конвертировать в хеш перед слиянием

params.require(:model).permit(:name).to_h.merge(attribute_1: @attr_1, attribute_2: @attr_2)

Вы должны быть очень уверены, что назначаете непользовательский ввод, хотя в противном случае вы отрицаете цель сильных параметров.

person Tyrone Wilson    schedule 15.08.2018