Rails 4.0 con Devise. Atributos nesteds Parámetros no permitidos

Estoy trabajando en una aplicación web utilizando Devise and Rails 4. Tengo un modelo de usuario que he ampliado con 2 campos de formulario adicionales, de modo que cuando un usuario se registra, también puede enviar sus primeros / apellidos. (basado en http://blog.12spokes.com/web-design-development/adding-custom-fields-to-your-devise-user-model-in-rails-4/ ). Ahora quiero agregar un modelo de Institución . Este modelo tiene_muchos : usuarios y un usuario pertenece_ a : institución. Quiero poder registrar el nombre de la institución en el mismo formulario en el que inscribo al usuario. Sé que necesito un atributo nested en mi modelo institucional , ya que este es el padre, que mostraré en un momento. Cuando bash registrar al usuario, aparece en la consola: Parámetros no permitidos: Instituciones .

Mi sugerencia es que no puedo actualizar mi clase para padres (Institución) en función de mi clase secundaria (Usuario). ¿Podría haber una solución para esto? ¿O alguien ha experimentado algo similar?

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 Aquí tengo la forma anidada

  resource_name, :url => registration_path(resource_name)) do |f| %>  . .  


Basado en el tutorial que he vinculado anteriormente, he creado un nuevo User :: ParameterSanitizer que hereda de Devise :: ParameterSanitizer y sign_up método sign_up siguiente manera:

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 

Finalmente, mi 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 

¡Gracias por leer!

Salida de parámetros de la consola:

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

EDITAR

Como se sugirió, agregué

 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 )* 

PERO, si reedito a

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

No obtengo ningún error de syntax, pero obtengo los parámetros No permitidos: Instituciones en la Solicitud.

Mi creencia es que esto sucede porque el Usuario es un hijo de la Institución. Sin embargo, no he podido encontrar una solución a esto.

config / routes.rb

Cree su propio controlador de registro como … ( consulte la documentación de diseño para los detalles de los controladores que prevalecen aquí … ) … que es la forma más elegante en lugar de hacerlo a través de ApplicationController

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

app / controllers / users / registrations_controller.rb

Anule el nuevo método para crear un Profile asociado con el modelo de User como se indica a continuación … ejecute el método configure_permitted_parameters antes para desinfectar los parámetros ( tenga en cuenta cómo agregar parámetros nesteds )

 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

Esta es la migración que genera el modelo de Profile ( tenga en cuenta la referencia al User ) … este perfil de ejemplo solo mantiene el nombre fullname como una extensión del User pero no dude en agregarlo como lo desee.

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

app / 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 

app / models / profile.rb

 class Profile < ActiveRecord::Base # Associations belongs_to :user # Validations validates :fullname, presence: true end 

app / views / idear / registraciones / new.html

 < % resource.build_profile if resource.profile.nil? %> < %= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %> 
    < %= devise_error_messages! %>
  • < %= f.fields_for :profile do |profile_fields| %> < %= profile_fields.label :fullname %> < %= profile_fields.text_field :fullname %> < % end %>
  • < %= f.label :password %> < %= f.password_field :password %>
  • < %= f.label :password_confirmation %> < %= f.password_field :password_confirmation %>
  • < %= f.submit %>
  • < %= render "devise/shared/links" %>

< % end %>

Debe crear su propio controlador de registro para hacerlo, así es cómo:

routes.rb

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

Controlador

Debe reemplazar :your_fields por los campos que desea permitir (lo siento si se lo dejo a usted, pero eso hace que mi respuesta sea más general y, por lo tanto, utilizable para cualquier persona que pase)

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

Información adicional (atributos nesteds + algunas pruebas)

También tenga en cuenta que si está utilizando asociación y accepts_nested_attributes_for tendrá params estructurados como este

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

Y, por supuesto, debes usar la misma estructura en tu método sign_up_params . Si necesita comprender esto, puede cambiar el contenido del método sign_up_params como este:

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

Eso permitirá cualquier parámetro, luego publique su formulario (debe pasar esta vez) y mire en la consola de los Rails para ver la estructura de los params , finalmente puede configurar el método sign_up_params correctamente

Comprueba esto para obtener más información http://www.railsexperiments.com/using-strong-parameters-with-nested-forms/

En tu caso deberías usar:

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

Usar rails 5.1 y diseñar 4.4.1 a continuación es el más corto y funciona bastante bien:

app / models / user.rb

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

app / controllers / 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 

La clave aquí es que puede hacer lo siguiente sin hacer un controlador por separado:

  • permitir atributos nesteds
  • construir la relación para el generador de formularios