Rails devise doesn't save extra input fields t

2019-08-12 04:03发布

问题:

I'm having problem with inserting extra user fields values to database with Devise gem. What i'm missing?

I'm using

ruby 2.1.5p273
Rails 4.1.8

This is my user.rb model

class User < ActiveRecord::Base

  belongs_to :country

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :confirmable, :authentication_keys => [:login]

  attr_accessor :login, :name, :last_name, :address, :post_number, :city, :mobile

  validates :username, presence: true, length: {maximum: 25}, :uniqueness => { :case_sensitive => false }
  validates :name, presence:true
  validates :last_name, presence:true
  validates :address, presence:true
  validates :post_number, presence:true
  validates :city, presence:true
  validates :country, presence:true
  validates :mobile, presence:true

  def login=(login)
    @login = login
  end

  def login
    @login || self.username || self.email
  end

  def self.find_for_database_authentication(warden_conditions)
    conditions = warden_conditions.dup
    if login = conditions.delete(:login)
      where(conditions).where(conditions).where(["username = :value OR lower(email) = lower(:value)", { :value => login }]).first
    else
      where(conditions).first
    end
  end

end

My aplication controller

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_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) << :username << :email << :password << :password_confirmation << :remember_me << :name << :last_name << :address << :post_number << :city << :country_id << :mobile
    devise_parameter_sanitizer.for(:sign_in) << :login << :username << :email << :password << :remember_me
    devise_parameter_sanitizer.for(:account_update) << :username << :email << :password << :password_confirmation << :current_password << :name << :last_name << :address << :post_number << :city << :country_id << :mobile
  end

end

Application controller (V2, i tried boat ways)

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_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :password, :password_confirmation, :remember_me, :name, :last_name, :address, :post_number, :city, :country_id, :mobile) }
    devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:login, :username, :email, :password, :remember_me) }
    devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:username, :email, :password, :password_confirmation, :current_password, :name, :last_name, :address, :post_number, :city, :country_id, :mobile) }
  end

end

Registratiions/new.html.erb

<h2>Sign up</h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= devise_error_messages! %>

    <div class="field">
      <%= f.label :email %><br />
      <%= f.email_field :email %>
    </div>

    <div class="field">
      <%= f.label :username %><br />
      <%= f.text_field :username, autofocus: true %>
    </div>

    <div class="field">
        <%= f.label :password %>
        <% if @validatable %>
        <em>(<%= @minimum_password_length %> characters minimum)</em>
        <% end %><br />
        <%= f.password_field :password, autocomplete: "off" %>
    </div>

    <div class="field">
        <%= f.label :password_confirmation %><br />
        <%= f.password_field :password_confirmation, autocomplete: "off" %>
    </div>

    <hr>

    <div>
        <%= f.label :name %><br />
        <%= f.text_field :name %>
    </div>

    <div>
        <%= f.label :last_name %><br />
        <%= f.text_field :last_name %>
    </div>
    <div>
        <%= f.label :address %><br />
        <%= f.text_field :address %>
    </div>
    <div>
        <%= f.label :post_number %><br />
        <%= f.text_field :post_number %>
    </div>
    <div>
        <%= f.label :city %><br />
        <%= f.text_field :city %>
    </div>
    <div>
        <%= f.label :country_id %><br />
        <%= f.select(:country_id, options_from_collection_for_select(Country.all, :id, :name)) %>
    </div>
    <div>
        <%= f.label :mobile %><br />
        <%= f.text_field :mobile %>
    </div>

    <br><br>

  <div class="actions">
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>

And this is a log what happens when i submit my form

Started POST "/users" for 127.0.0.1 at 2015-01-12 21:29:36 +0100
Processing by RegistrationsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"YXPl+X5Xpib9yG4ywUY2BAOmiGdNUtkQZtIgxqBXbak=", "user"=>{"email"=>"test@gmail.com", "username"=>"testUser", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "name"=>"Test", "last_name"=>"Test", "address"=>"Test", "post_number"=>"21332", "city"=>"TestCity", "country_id"=>"81831761", "mobile"=>"333666333666"}, "commit"=>"Sign up"}
   (0.1ms)  begin transaction
  User Exists (0.2ms)  SELECT  1 AS one FROM "users"  WHERE "users"."email" = 'test@gmail.com' LIMIT 1
  User Exists (0.1ms)  SELECT  1 AS one FROM "users"  WHERE LOWER("users"."username") = LOWER('testUser') LIMIT 1
  Country Load (0.0ms)  SELECT  "countries".* FROM "countries"  WHERE "countries"."id" = ? LIMIT 1  [["id", 81831761]]
  User Load (0.2ms)  SELECT  "users".* FROM "users"  WHERE "users"."confirmation_token" = '0ddbdfeadb0c6758be3dda41b068fd319e0fd63cefcb16c5b985f90ab16e6784'  ORDER BY "users"."id" ASC LIMIT 1
Binary data inserted for `string` type on column `confirmation_token`
Binary data inserted for `string` type on column `encrypted_password`
  SQL (0.3ms)  INSERT INTO "users" ("confirmation_sent_at", "confirmation_token", "country_id", "created_at", "email", "encrypted_password", "updated_at", "username") VALUES (?, ?, ?, ?, ?, ?, ?, ?)  [["confirmation_sent_at", "2015-01-12 20:29:36.962365"], ["confirmation_token", "0ddbdfeadb0c6758be3dda41b068fd319e0fd63cefcb16c5b985f90ab16e6784"], ["country_id", 81831761], ["created_at", "2015-01-12 20:29:36.742990"], ["email", "test@gmail.com"], ["encrypted_password", "$2a$10$bL6dOOVGzfJ5eOGA8mU/ces7w1CCjJk8Z8rlj6BvSQyqbLgazqrVW"], ["updated_at", "2015-01-12 20:29:36.742990"], ["username", "testUser"]]
  Rendered devise/mailer/confirmation_instructions.html.erb (0.8ms)

I have two questons:

  1. What i have to do, so devise will extend his INSERT sql query to insert extra database fields values?
  2. Which way of writing application controller devise_parameter_sanitizer is correct?

Thx for help

回答1:

I found a solution! In rails 4 you have to specifiey user_params in controller

This is my users_controller (added user_params)

class UsersController < ApplicationController

  def index
    @users = User.all
  end

  def new
    @user = User.new
  end

  private

  def user_params
    params.require(:user).permit(:username, :email, :password, :password_confirmation, :remember_me, :name, :last_name, :address, :post_number, :city, :country_id, :mobile)
  end


end

And this is my user model (removed attr_accessor)

    class User < ActiveRecord::Base

      belongs_to :country

      # Include default devise modules. Others available are:
      # :confirmable, :lockable, :timeoutable and :omniauthable
      devise :database_authenticatable, :registerable,
             :recoverable, :rememberable, :trackable, :validatable, :confirmable, :authentication_keys => [:login]

      validates :username, presence: true, length: {maximum: 25}, :uniqueness => { :case_sensitive => false }
      validates :name, presence:true, length: {maximum: 30}
      validates :last_name, presence:true
      validates :address, presence:true
      validates :post_number, presence:true
      validates :city, presence:true
      validates :country, presence:true
      validates :mobile, presence:true

      def login=(login)
        @login = login
      end

      def login
        @login || self.username || self.email
      end

      def self.find_for_database_authentication(warden_conditions)
        conditions = warden_conditions.dup
        if login = conditions.delete(:login)
          where(conditions).where(conditions).where(["username = :value OR lower(email) = lower(:value)", { :value => login }]).first
        else
          where(conditions).first
        end
      end

    end

And this is my application controller

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_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) do |u| u.permit(:username, :email, :password, :password_confirmation, :remember_me, :name, :last_name, :address, :post_number, :city, :country_id, :mobile) end
    devise_parameter_sanitizer.for(:sign_in) do |u| u.permit(:login, :username, :email, :password, :remember_me) end
    devise_parameter_sanitizer.for(:account_update) do |u| u.permit(:username, :email, :password, :password_confirmation, :current_password, :name, :last_name, :address, :post_number, :city, :country_id, :mobile) end
  end

end