2

I've got a modified Devise "sign_up" form (new registration) that includes fields for child and grandchild objects to be created along with the user. All of the model relationships are set up properly with access to the child's attributes. However, when the form renders, only the fields for the Devise user and one of the grandchildren is shown.

When a User is created, he/she will automatically be assigned a Customer object, an Account object, and an Address object. As you can see by the relationships in the User model below, User has one Customer and Customer has many Accounts and one Address. There was previously an issue with rendering the form at all, which I solved by changing the values passed to the builder method. WHY WON'T THIS WORK??? This is what I have so far:

*user.rb

class User < ActiveRecord::Base
  before_create :generate_id

  # Virtual attribute for authenticating by either username or email
  # This is in addition to a real persisted field like 'username'
  attr_accessor :login

  has_one :customer, :dependent => :destroy
  has_many :accounts, through: :customer
  accepts_nested_attributes_for :customer, :allow_destroy => true
  accepts_nested_attributes_for :accounts, :allow_destroy => true

  has_one :address, through: :customer
  accepts_nested_attributes_for :customer, :allow_destroy => true
  accepts_nested_attributes_for :address, :allow_destroy => true

  has_one :administrator

  validates_uniqueness_of :email, :case_sensitive => false
  validates_uniqueness_of :id
  validates :username,
    :presence => true,
    :uniqueness=> {
        :case_sensitive => false
    }

  # User ID is a generated uuid
  include ActiveUUID::UUID
  natural_key :user_id, :remember_created_at
  belongs_to :user
  # specify custom UUID namespace for the natural key
  uuid_namespace "1dd74dd0-d116-11e0-99c7-5ac5d975667e"

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

  # Generate a random uuid for new user id creation
  def generate_id
    self.id = SecureRandom.uuid
  end

  # Allow signin by either email or username ("lower" function might have to be removed?)
  def self.find_for_database_authentication(warden_conditions)
      conditions = warden_conditions.dup
      if login = conditions.delete(:login)
        where(conditions.to_h).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
      else
        where(conditions.to_h).first
      end
  end  
end

registrations_controller.rb

class RegistrationsController < Devise::RegistrationsController

    before_filter :configure_permitted_parameters

    # GET /users/sign_up
  def new
  @user = User.new

  build_resource({})
  self.resource[:customer => Customer.new, :account => Account.new, :address => Address.new]
    respond_with self.resource
  end

  def create
  @user = User.new
  # Override Devise default behavior and create a customer, account, and address as well

  resource = build_resource(params[:sign_up])

  if(resource.save)
    sign_in(resource_name, resource)
    respond_with resource, :location => after_sign_up_path_for(resource)
  else
    render :action => "new"
  end
  end

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) { |u|
      u.permit(:username, :email, :password, :password_confirmation,
               customer_attributes: [:title, :firstname, :lastname, :phone1, :phone2],
               account_attributes: [:acct_type],
               address_attributes: [:address1, :address2, :zip_code])
    }
  end
end

application_controller.rb

class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception

  before_filter :configure_permitted_parameters, if: :devise_controller?

  def after_sign_in_path_for(resource)
    if current_user.role == 'admin'
        adminview_administrator_path(current_user, format: :html)
    else
        accounts_path(current_user, format: :html)
    end
  end

  protected
    def configure_permitted_parameters
        devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :password, :password_confirmation,
                                            customer_attributes: [:title, :firstname, :lastname, :phone1, :phone2],
                                            account_attributes: [:acct_type],
                                            address_attributes: [:address1, :address2, :zip_code]) }
        devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:login, :username, :email, :password) }
        devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:username, :email, :password, :password_confirmation, :current_password) }
    end
end

views/devise/registrations/new.html.erb

<h1>Create an account</h1>
  <div class="panel panel-default" style="width: 50%; padding: 0 25px;">  

    <%= bootstrap_nested_form_for(resource, as: resource_name, url: user_registration_path(resource_name)) do |f| %>
      <%= devise_error_messages! %>
      <h3>User Info</h3>
        <!-- fields for User object -->
        <%= f.text_field :username, autofocus: true %>
        <%= f.email_field :email %>
        <%= f.password_field :password , autocomplete: "off"%>
          <% if @validatable %>
            <em>(<%= @minimum_password_length %> characters minimum)</em>
          <% end %><br />
        <%= f.password_field :password_confirmation, autocomplete: "off" %>

        <!-- fields for Customer object -->
        <%= f.fields_for :customer do |customer_fields| %>
          <%= customer_fields.text_field :title %>
          <%= customer_fields.text_field :firstname %>
          <%= customer_fields.text_field :lastname %>
          <%= customer_fields.text_field :phone1 %>
          <%= customer_fields.text_field :phone2 %>
        <% end %>

        <!-- fields for Account object -->
        <%= f.fields_for :account do |account_fields| %>
          <%= account_fields.text_field :acct_type %>
        <% end %>

        <!-- fields for Address object -->
        <%= f.fields_for :address do |address_fields| %>
          <%= address_fields.text_field :address1 %>
          <%= address_fields.text_field :address2 %>
          <%= address_fields.text_field :zip_code %>
        <% end %>
      <br />
      <div class="actions">
        <%= f.submit "Create My Account", :class => "btn btn-info" %>
      </div>
      <% end %>
  </div>
</div>

Again, the above view does render, but the form only displays the fields for Devise new user and the one field (acct_type) for the account fields. How to get the rest of form to display and create all of these things on submission? Everything I've tried and everything I've read leads me to think that there's a problem with Rails 4's strong_parameters not being able to recognize the permitted attributes (see above controllers) in an array. Could that be the issue? If so, how does one go about passing the parameters necessary to build all these things?

Could be problem with the routes? routes.rb

Rails.application.routes.draw do
  devise_for :users, :controllers => { :registrations => "registrations" }

  devise_scope :user do
    # authentication
    post "/accounts/adminview" => "devise/sessions#new"
  end

  root 'home#index'

  resources :administrators do
    member do
      get :adminview
    end
  end

  resources :users do
    resource :customers
    resource :accounts
    resource :addresses
  end

  resources :account_types, :accounts, :addresses, :administrators, :customers, :transaction_types, :transactions, :users

end

I've tried every combination of ways that I could find on SO. This has taken up days worth of valuable time. I don't see any reason why it can't work. Does anyone have a better way of doing this? Is there a gem that would help? I'm willing to tear Devise apart and rebuild if necessary.

F.Y.I. It's Rails 4 and Devise 3.4.1. I've also added nested_form gem, but it doesn't make a difference.

Thank you

B. Bulpett
  • 814
  • 12
  • 27

1 Answers1

0

If you raise your params in controller you probably see accounts_attributes instead account_attributes you are setting in permit at application_controller, try replace it.