Рельсы 4.0 с Devise. Вложенные атрибуты Недопустимые параметры

Я работаю над веб-приложением, используя Devise и Rails 4. У меня есть модель User, которую я расширил двумя дополнительными полями формы, так что, когда пользователь регистрируется, он также может отправить свой первый/последний имена. (на основе http://blog.12spokes.com/web-design-development/adding-custom-fields-to-your-devise-user-model-in-rails-4/). Теперь я хочу добавить модель Institution. Эта модель имеет_многие :users и пользователь принадлежит_к :institution. Я хочу иметь возможность зарегистрировать название учреждения в той же форме, что и пользователь. Я знаю, что мне нужен вложенный_атрибут в моей модели Institution, так как это родитель, который я покажу чуть позже. Когда я пытаюсь зарегистрировать пользователя, я получаю в консоли: Недопустимые параметры: учреждения.

Мой намек на то, что я не могу обновить свой родительский класс (учреждение) на основе моего дочернего класса (пользователя). Может ли быть решение этого? Или кто-нибудь сталкивался с чем-то подобным?

class Institutions < ActiveRecord::Base
    has_many :users, 
    accepts_nested_attributes_for :users
end

class User < ActiveRecord::Base
     devise :database_authenticatable, :registerable,
     :recoverable, :rememberable, :trackable, :validatable
     belongs_to :institution
end

registrations/new.html.erb Здесь у меня есть вложенная форма

<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f|     %>
<%= devise_error_messages! %>
.
. 
    <%= f.fields_for :institutions do |i| %>
        <p><%= i.label :name %><br />
        <%= i.text_field :institutions_attr %></p>
    <% end %>

Основываясь на учебнике, на который я ссылался ранее, я создал новый User::ParameterSanitizer, который наследуется от Devise::ParameterSanitizer и переопределяет sign_up. > метод следующим образом:

lib/user_sanitizer.rb

private
def sign_up
    default_params.permit(:first_name, :last_name ,:email, :password,  :password_confirmation, :current_password, institutions_attributes: [:id, :name])
end

Наконец, мой application_controller.rb

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  protected
  def devise_parameter_sanitizer
    if resource_class == User
    User::ParameterSanitizer.new(User, :user, params)
    else 
    super
    end
  end
end

Спасибо за чтение!

Вывод параметров консоли:

{"utf8"=>"✓",
 "authenticity_token"=>"JKuN6K5l0iwFsj/25B7GKDj7WEHR4DO3oaVyGxGJKvU=",
 "user"=>{"email"=>"[email protected]",
 "first_name"=>"abc",
 "last_name"=>"xyz",
 "institutions"=>{"name"=>"Government"},
 "password"=>"[FILTERED]",
 "password_confirmation"=>"[FILTERED]"},
 "commit"=>"Sign up"}

ИЗМЕНИТЬ

Как было предложено, я добавил

params.require(resource_name).permit( :email, :first_name, :last_name, institution:  [:name], :password, :password_confirmation ) and I get an *error syntax error, unexpected ',', expecting => ...nstitution: [:name], :password, :password_confirmation )*

НО, если я повторно отредактирую

params.require(resource_name).permit( :email, :first_name, :last_name, :password, :password_confirmation, institution:  [:name] ) 

Я не получаю синтаксической ошибки, но получаю неразрешенные параметры: учреждения в запросе.

Я считаю, что это происходит потому, что Пользователь является дочерним элементом Учреждения. Однако мне не удалось найти обходной путь.


person Claudiu S    schedule 20.07.2013    source источник


Ответы (3)


config/routes.rb

Создайте свой собственный контроллер регистрации, например... (подробности переопределения контроллеров см. в документации Devise здесь.. .) ... что более элегантно, чем делать это через ApplicationController

devise_for :users, controllers: {registrations: 'users/registrations'}

app/controllers/users/registrations_controller.rb

Переопределите новый метод, чтобы создать Profile, связанный с моделью User, как показано ниже... запустите метод configure_permitted_parameters перед очисткой параметров (обратите внимание, как добавить вложенные параметры)

class Users::RegistrationsController < Devise::RegistrationsController

  before_filter :configure_permitted_parameters

  # GET /users/sign_up
  def new

    # Override Devise default behaviour and create a profile as well
    build_resource({})
    resource.build_profile
    respond_with self.resource
  end

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) { |u|
      u.permit(:email, :password, :password_confirmation, :profile_attributes => :fullname)
    }
  end
end

db/migrate/xxxxxxxxxxxxxxx_create_profiles.rb

Это миграция, которая создает модель Profile (обратите внимание на ссылку на User)... этот пример профиля сохраняет только fullname как расширение User, но не стесняйтесь добавлять по своему усмотрению!

class CreateProfiles < ActiveRecord::Migration
  def change
    create_table :profiles do |t|
       t.references :user
       t.string :fullname
       t.timestamps
    end
  end
end

приложение/models/user.rb

class User < ActiveRecord::Base

  # Associations
  has_one :profile, dependent: :destroy, autosave: true

  # Allow saving of attributes on associated records through the parent,
  # :autosave option is automatically enabled on every association
  accepts_nested_attributes_for :profile

  # Devise
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
end

приложение/models/profile.rb

class Profile < ActiveRecord::Base

  # Associations
  belongs_to :user

  # Validations
  validates :fullname, presence: true
end

app/views/devise/registrations/new.html

<% resource.build_profile if resource.profile.nil? %>
<%= form_for(resource, :as => resource_name,
                       :url => registration_path(resource_name)) do |f| %>
  <ul>

    <%= devise_error_messages! %>

    <li class="fullname">
      <%= f.fields_for :profile do |profile_fields| %>
        <%= profile_fields.label :fullname %>
        <%= profile_fields.text_field :fullname %>
      <% end %>
    </li>
    <li class="email">
      <%= f.label :email %>
      <%= f.email_field :email, :autofocus => true %>
    </li>
    <li class="password">
      <%= f.label :password %>
      <%= f.password_field :password %>
    </li>
    <li class="password">
      <%= f.label :password_confirmation %>
      <%= f.password_field :password_confirmation %>
    </li>
    <li>
      <%= f.submit %>
    </li>
    <li>
      <p><%= render "devise/shared/links" %></p>
    </li>
  </ul>
<% end %>
person King'ori Maina    schedule 19.12.2013
comment
Большое спасибо King'ori, это именно то, что я искал! Сэкономил мне несколько лишних часов разочарования. - person David Routen; 30.09.2015
comment
@king'ori Maina Я использую драгоценный камень devise_auth_token, и после падения вышеописанных методов для того же драгоценного камня я все еще получаю неразрешенный параметр: ошибка учетной записи в моем журнале сервера - person Anjan; 01.10.2015
comment
@Anjan К сожалению, я раньше не использовал devise_auth_token, поэтому не знаю, с чего начать. Я предполагаю, что ошибка Unpermitted parameter: account означает, что параметр :account необходимо добавить к разрешенным параметрам. См. метод configure_permitted_parameters в классе Users::RegistrationsController выше, чтобы узнать, как я добавил атрибуты профиля. - person King'ori Maina; 01.10.2015
comment
Этот ответ должен быть принят как правильный ответ! - person Sagar Ranglani; 05.01.2016

Для этого вы должны создать свой собственный контроллер регистрации. Вот как это сделать:

routes.rb

devise_for :users, controllers: {registrations: 'registrations'}

Контроллер

Вы должны заменить :your_fields полями, которые вы хотите разрешить (извините, если я оставляю это вам, но это делает мой ответ более общим, поэтому его можно использовать для всех, кто пройдет мимо)

class RegistrationsController < Devise::RegistrationsController

  private

    def sign_up_params
      allow = [:email, :your_fields, :password, :password_confirmation]
      params.require(resource_name).permit(allow)
    end

end

Дополнительная информация (вложенные атрибуты + некоторое тестирование)

Также обратите внимание, что если вы используете ассоциацию и accepts_nested_attributes_for, у вас будет params такая структура

model: {field, field, field, associated_model: {field, field}}

И, конечно же, вы должны использовать ту же структуру в своем методе sign_up_params. Если вам нужно понять это, вы можете изменить содержимое метода sign_up_params следующим образом:

    def sign_up_params
      params.require(resource_name).permit!
    end

Это разрешит любой параметр, затем опубликуйте свою форму (на этот раз она должна пройти) и загляните в консоль rails, чтобы увидеть структуру params, наконец, вы сможете правильно настроить метод sign_up_params

Проверьте это для получения дополнительной информации http://www.railsexperiments.com/using-strong-parameters-with-nested-forms/

В вашем случае вы должны использовать:

params.require(resource_name).permit( :email, :first_name, :last_name, institutions: [:name], :password, :password_confirmation )

person Benj    schedule 20.07.2013
comment
Спасибо за ответ. Я попробовал это. I не работает, так как он по-прежнему не разрешает параметры модели Institution. - person Claudiu S; 21.07.2013
comment
Я видел это ^_^. Я написал это следующим образом: allow = [:email, :first_name, :last_name, :password, :password_confirmation, :name], где :name — поле в другой модели. - person Claudiu S; 21.07.2013
comment
Я предполагаю, что это как-то связано с тем, что Пользователь является дочерним элементом Учреждения. Или, может быть, я ошибаюсь - person Claudiu S; 21.07.2013
comment
Кстати, часть params.require(resource_name) опирается на тот факт, что User является «родительской» моделью, но в вашем случае вы должны заменить resource_name на :institution. С другой стороны, я не знаю, как на это отреагирует devise, но это уже другой вопрос (мы ведь говорим о сильных параметрах, верно?) - person Benj; 21.07.2013
comment
Да, мы говорим о сильных параметрах, и да, пользователь в данном случае является дочерним. Я заменил имя_ресурса, и этот параметр не найден: учреждение, поэтому я предполагаю, что он не работает. - person Claudiu S; 21.07.2013
comment
параметр не найден: учреждение означает, что параметры не содержат :учреждение или пусты. Я предполагаю, что вы не последовали моему совету по тестированию с .permit!. Вы должны найти эту часть своего ответа самостоятельно сейчас (я не могу проверить это для вас) - person Benj; 21.07.2013
comment
Я сделал именно это: params.require(:institution).permit! и ошибка исходит от ActionController::ParameterMissing в RegistrationsController#create. А в запросе получаю вот это заведение=›{name=›xyz} - person Claudiu S; 21.07.2013
comment
Вставьте всю строку параметров, найденную в консоли, пожалуйста (отредактируйте свой вопрос, это более читабельно) - person Benj; 21.07.2013
comment
Хорошо, отредактировал мой ответ, надеюсь, это все, потому что мне пора идти :) удачи - person Benj; 21.07.2013
comment
Без проблем. Вы можете дать мне репутацию, приняв ответ, вот как работает stackoverflow :) ура - person Benj; 21.07.2013
comment
Я все еще использую одну и ту же технику во всех своих проектах, сохраняя их до последней версии рельсов. работает подтверждаю - person Benj; 28.01.2015

Использование rails 5.1 и devise 4.4.1 является самым коротким и работает довольно хорошо:

приложение/models/user.rb

after_initialize do
  build_profile if new_record? && profile.blank?
end

приложение/контроллеры/application_controller.rb

before_action :configure_permitted_parameters, if: :devise_controller?

def configure_permitted_parameters
  devise_parameter_sanitizer.permit(:sign_up, keys: [{ profile_attributes: :name }])
end

Ключевым моментом здесь является то, что вы можете сделать следующее, не создавая отдельный контроллер:

  • разрешить вложенные атрибуты
  • построить отношение для конструктора форм
person Aleksandr K.    schedule 16.02.2018
comment
Это было очень полезно - person Promise Preston; 20.08.2020