I've been looking at my code for some time now and can't work out where the errors are with Devise. I've added a few additional lines to the Sign Up view and changed everything as described in the Devise Readme, but the additional fields are not showing up, only the usual Email, Password and Confirm Password fields are. My users are called Knockers, in case anyone was wondering!
Here is my views page (registrations\new.html.erb):
<h2>Sign up</h2>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.text_field :first_name %></div>
<div><%= f.text_field :last_name %></div>
<div><%= f.text_field :username %></div>
<div><%= f.text_field :gender %></div>
<div><%= f.date_field :dob %></div>
<div><%= f.label :email %><br />
<%= f.email_field :email, :autofocus => true %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<div><%= f.submit "Sign up" %></div>
<% end %>
<% if knocker_signed_in? %>
Signed in as <%= current_knocker.name %>. Not you?
<%= link_to "Sign out", destroy_knocker_session_path,:method => :delete %>
<% else %>
<%= link_to "Sign up", new_knocker_registration_path %> or
<%= link_to "Sign in", new_knocker_session_path %>
<%= link_to "Sign in with Facebook", knocker_omniauth_authorize_path(:facebook) %>
<% end %>
<% if knocker_signed_in? %>
Signed in as <%= current_knocker.name %>. Not you?
<%= link_to "Sign out", destroy_knocker_session_path,:method => :delete %>
<% else %>
<%= link_to "Sign up", new_knocker_registration_path %> or
<%= link_to "Sign in", new_knocker_session_path %>
<%= link_to "Sign in with Twitter", knocker_omniauth_authorize_path(:twitter) %>
<% end %>
<% if knocker_signed_in? %>
Signed in as <%= current_knocker.name %>. Not you?
<%= link_to "Sign out", destroy_knocker_session_path,:method => :delete %>
<% else %>
<%= link_to "Sign up", new_knocker_registration_path %> or
<%= link_to "Sign in", new_knocker_session_path %>
<%= link_to "Sign in with Google", knocker_omniauth_authorize_path(:google_oauth2) %>
<% end %>
<%= render "knockers/shared/links" %>
My model page (\knocker.rb)
Class Knocker < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:facebook, :twitter, :google_oauth2]
before_save { self.username = username.downcase }
validates :first_name, presence: true,
length: { maximum: 25 }
validates :last_name, presence: true,
length: { maximum: 25 }
VALID_USERNAME_REGEX = /\A[\w+\-._]+\z/i
validates :username, presence: true,
length: { maximum: 20 },
format: { with: VALID_USERNAME_REGEX },
uniqueness: { case_sensitive: false }
validates :email, presence: true
validates :password, presence: true
validates :dob, presence: true
validates :gender, presence: true
validates :postcode, presence: true
def self.find_for_facebook_oauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do |knocker|
knocker.provider = auth.provider
knocker.uid = auth.uid
knocker.email = auth.info.email
knocker.password = Devise.friendly_token[0,20]
#knocker.first_name = auth.info.name # assuming the user model has a name
#knocker.image = auth.info.image # assuming the user model has an image
end
end
def self.find_for_twitter_oauth(auth, signed_in_resource=nil)
knocker = Knocker.where(:provider => auth.provider, :uid => auth.uid).first
if knocker
return knocker
else
registered_knocker = Knocker.where(:email => auth.uid + "#twitter.com").first
if registered_knocker
return registered_knocker
else
knocker = Knocker.create(full_name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.uid+"#twitter.com",
password:Devise.friendly_token[0,20],
username:auth.info.nickname,
about:auth.info.description )
end
end
end
def self.find_for_google_oauth2(access_token, signed_in_resource=nil)
data = access_token.info
knocker = Knocker.where(:provider => access_token.provider, :uid => access_token.uid ).first
if knocker
return knocker
else
registered_knocker = Knocker.where(:email => access_token.info.email).first
if registered_knocker
return registered_knocker
else
knocker = Knocker.create(first_name: data["first_name"],
last_name: data["last_name"],
provider:access_token.provider,
email: data["email"],
uid: access_token.uid ,
password: Devise.friendly_token[0,20],
gender: data["gender"],
dob: data["birthday"]
)
end
end
end
def self.new_with_session(params, session)
super.tap do |knocker|
if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
knocker.email = data["email"] if knocker.email.blank?
end
end
end
def name
first_name + " " + last_name
end
end
My database migration:
class DeviseCreateKnockers < ActiveRecord::Migration
def change
create_table(:knockers) do |t|
t.string :first_name, :null => false
t.string :last_name, :null => false
t.string :username, :null => false
t.string :town
t.string :postcode
t.float :latitude
t.float :longitude
t.date :dob, :null => false
t.text :about
t.string :gender, :null => false
t.string :nationality
## Database authenticatable
t.string :email, :null => false, :default => ""
t.string :encrypted_password, :null => false, :default => ""
## Omniauth data
t.string :provider
t.string :uid
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, :default => 0, :null => false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
t.integer :failed_attempts, :default => 0, :null => false # Only if lock strategy is :failed_attempts
t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps
end
add_index :knockers, :email, :unique => true
add_index :knockers, :reset_password_token, :unique => true
add_index :knockers, :username, :unique => true
add_index :knockers, :latitude
add_index :knockers, :longitude
add_index :knockers, :dob
# add_index :knockers, :confirmation_token, :unique => true
# add_index :knockers, :unlock_token, :unique => true
end
end
and finally 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_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :first_name, :last_name, :gender, :dob, :email, :password, :password_confirmation) }
end
end
Any help would be greatly appreciated. I've looked on here through a few problems, but most of them just seem to be in getting the wrong field type... can't see what the problem is here unless it's something to do with a badly implemented OAuth problem.
Rename the knockers folder under application_name/app/views directory to devise.
While generating the views you by mistake generated them as rails generate devise:views knockers instead of rails generate devise:views.
You use rails generate devise:views knockers to generate views if you have multiple devise models and you need different views(customizations) for them.
Related
I have an exercise called "BillApp" basically it's a Bill that have some products, and i should could make bills, calculate IVA, etc.
I have the next schema:
create_table "bill_items", force: :cascade do |t|
t.integer "amount"
t.integer "product_id"
t.integer "bill_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["bill_id"], name: "index_bill_items_on_bill_id"
t.index ["product_id"], name: "index_bill_items_on_product_id"
end
create_table "bills", force: :cascade do |t|
t.string "user_name"
t.string "dni"
t.date "expiration"
t.float "sub_total"
t.float "grand_total"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "products", force: :cascade do |t|
t.string "name"
t.string "description"
t.float "price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Bill model:
class Bill < ApplicationRecord
has_many :bill_items
has_many :products, through: :bill_items
accepts_nested_attributes_for :bill_items
end
BillItem model:
class BillItem < ApplicationRecord
belongs_to :product
belongs_to :bill
end
Product model:
class Product < ApplicationRecord
has_many :bill_items
has_many :bills, through: :bill_items
end
The ProductsController is a normal one, generated by scaffold, it doesn't matter.
BillsController:
class BillsController < ApplicationController
before_action :set_bill, only: [:show, :update, :destroy, :edit]
def new
#bill = Bill.new
#bill.bill_items.build
end
def create
#bill = Bill.new(bill_params)
byebug
#bill.save
end
private
def set_bill
#bill = Bill.find(params[:id])
end
def bill_params
params.require(:bill).permit(:user_name, :dni, { bill_items_attributes: [:product_id, :amount, :bill_id] })
end
end
And finally the Bill new view:
<%= form_for(#bill) do |f| %>
<div>
<%= f.label :user_name %>
<%= f.text_field :user_name %>
</div>
<div>
<%= f.label :dni %>
<%= f.text_field :dni %>
</div>
<%= f.fields_for :bill_items do |fp| %>
<div>
<%= fp.label :product %>
<%= fp.collection_select :product_id, Product.all, :id, :name %>
</div>
<div>
<%= fp.label :amount %>
<%= fp.number_field :amount %>
</div>
<% end %>
<%= f.submit %></div>
<% end %>
The problem is very specific, in rails 5 when it try to call #bill.save it fails and in the errors it shows:
#<ActiveModel::Errors:0x007fd9ea61ed58 #base=#<Bill id: nil, user_name: "asd", dni: "asd", expiration: nil, sub_total: nil, grand_total: nil, created_at: nil, updated_at: nil>, #messages={:"bill_items.bill"=>["must exist"]}, #details={"bill_items.bill"=>[{:error=>:blank}]}>
But it works perfectly in Rails 4.2.6.
The entire project folder is here: https://github.com/TheSwash/bill_app
currentrly in the branch feature/bills_controller
Somebody have an idea about what's happening?
the problem it's the bill_items validation
In rails 5 the belongs_to association required by default
http://blog.bigbinary.com/2016/02/15/rails-5-makes-belong-to-association-required-by-default.html
I am trying to build a multitenant app using rails 4 and devise. The design I am attempting is that an account has one owner(primary user) and has many users. I have an account being added along with the nested form adding the user. I'm not sure if I have issue with the schema I'm attempting to populate or if I am missing a step that would allow me to add an account and user at the same time, with the account record being populated with the owner_id (primary user) and the user being populated with the account_id.
class Account < ActiveRecord::Base
cattr_accessor :current_id
has_many :users
has_one :owner, class_name: 'User'
validates :owner, presence: true
accepts_nested_attributes_for :owner
end
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :account
end
class AccountsController < ApplicationController
def new
#account = Account.new
#account.build_owner
end
def create
#account = Account.new(account_params)
if #account.save
redirect_to root_path, notice: 'Successfully Created Account!'
else
render action: 'new'
end
end
private
def account_params
params.require(:account).permit(:subdomain, owner_attributes: [:email, :password, :password_confirmation])
end
end
Views/Accounts/new.html.erb
<div class="col-md-6 col-md-offset-3 panel panel-default">
<div class="panel-body">
<h2>Create an Account</h2>
<%= form_for #account do |f| %>
<%= f.fields_for :owner do |o| %>
<%= form_group_for o, :email do %>
<%= o.text_field :email, class: 'form-control' %>
<% end %>
<%= form_group_for o, :password do %>
<%= o.password_field :password, class: 'form-control' %>
<% end %>
<%= form_group_for o, :password_confirmation do %>
<%= o.password_field :password_confirmation, class: 'form-control' %>
<% end %>
<% end %>
<%= form_group_for f, :name, opts= {:label => "Company Name"} do %>
<%= f.text_field :name, class: 'form-control' %>
<% end %>
<%= f.submit class: 'btn btn-primary' %>
<% end %>
</div>
</div>
App/Helper/Form_Helper (view call this)
module FormHelper
def errors_for(form, field)
content_tag(:p, form.object.errors[field].try(:first), class: 'help-block')
end
def form_group_for(form, field, opts={}, &block)
if opts.has_key?(:label)
label = opts.fetch(:label)
else
label = field.to_s
end
has_errors = form.object.errors[field].present?
content_tag :div, class: "form-group #{'has-error' if has_errors}" do
concat form.label(label, class: 'control-label')
concat capture(&block)
concat errors_for(form, field)
end
end
end
DB/schema.rb
ActiveRecord::Schema.define(version: 20140629124546) do
create_table "accounts", force: true do |t|
t.string "name"
t.integer "owner_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "clients", force: true do |t|
t.string "name", null: false
t.text "address"
t.integer "account_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "clients", ["account_id"], name: "index_clients_on_account_id"
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "account_id"
end
add_index "users", ["account_id"], name: "index_users_on_account_id"
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
Currently the design creates both the account and user records. The user record is populated with the account_id, but the account_id isn't populated with the owner_id (user_id).
This is likely in your associations, which are currently incomplete and a bit inaccurate.
If a model's attribute holds a foreign key, like the owner_id attribute on your Account model, then it belongs_to that model it is referencing. Think of it like a cattle brand. If you have the ids of other models on you, you belong to those models. You current have a has_one where you need a belongs_to.
You will also need to set up the inverse association, which is currently lacking in your user model.
Assuming you actually will only ever want a user to be associated with one account (as opposed to a user being on more than one account), you could rearrange your associations like this:
#models/account.rb
class Account < ActiveRecord::Base
has_many :users
belongs_to :owner, class_name: 'User'
accepts_nested_attributes_for :owner
end
#models/user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :account
has_one :owned_account, class_name: 'Account', foreign_key: 'owner_id'
end
Proper association setup will hopefully fix your issue.
I'm using devise gem with paperclip to handle authentication and upload pictures. Problem is that I have used paperclip on the same model twice for storing two pictures (let's call those paperclip columns :avatar , :superbadge), my model name is User.
Now, when I choose to upload two pictures, my rails application ignores the first file that I have chosen, instead It uses second chosen file and saves it in the first paperclip column, leaving second paperclip column blank. How do I fix it?
My application controller:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :configure_devise_permitted_parameters, if: :devise_controller?
protected
def configure_devise_permitted_parameters
registration_params = [:name, :email, :password, :password_confirmation,:avatar,:superstarbadge]
if params[:action] == "update"
devise_parameter_sanitizer.for(:account_update) {
|u| u.permit(registration_params << :current_password)
}
elsif params[:action] == "create"
devise_parameter_sanitizer.for(:sign_up) {
|u| u.permit(registration_params)
}
end
end
end
My User.rb model:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :validatable
has_attached_file :avatar, :styles => { :small => "100x100>" }
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
has_attached_file :superstarbadge, :styles => { :tiny => "100x100>" }, :default => "superbadge.jpg"
validates_attachment_content_type :superstarbadge, :content_type => /\Aimage\/.*\Z/
has_many :questions
has_many :answers
def to_s
email
end
end
My form_for for creating new user with devise gem (I'm using slim template language not erb):
h1 Sign up
= form_for(resource, as: resource_name, url: registration_path(resource_name), :html => { :multipart => true }) do |f|
= devise_error_messages!
.field
label= f.label :name
= f.text_field :name, autofocus: true
.field
label= f.label :email
= f.email_field :email, autofocus: true
.field
label= f.label :password
= f.password_field :password, autocomplete: 'off'
.field
label= f.label :password_confirmation
= f.password_field :password_confirmation, autocomplete: 'off'
.field
= f.label :avatar
= f.file_field :avatar
.field
= f.file_field :avatar
div
= f.submit "Sign up"
Both the file_field's in your form are referring to avatar field so when you submit the form the second chosen file (i.e., latest) gets saved as avatar. There is no file_field for superstarbadge so it never gets saved.
You need one file_field for avatar and other one for superstarbadge. So, your code should look like:
.field
= f.label :avatar
= f.file_field :avatar
.field
= f.file_field :superstarbadge ## This one should be superstarbadge and NOT avatar
I've been at this for days, and cannot figure out the problem. When I attempt to Sign Up, it appears to work, but when I check the rails console, it shows no username. I'm able to add one from there and it works fine, but using my Sign Up view it never works. Only the fields that were created when I installed Devise work. I've also added First and Last Name, but I only attempted to get Username working, so the code for names is incomplete. If I've left anything important out, let me know. I'd REALLY appreciate the help.
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
protected
def configure_devise_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) do |u|
u.permit(:email, :username, :password, :password_confirmation)
end
end
.
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates :username, :uniqueness => {:case_sensitive => false}
has_many :posts
has_many :offers
has_many :requests
has_one :profile
def name
"#{first_name} #{last_name}"
end
protected
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value", { :value => login.downcase }]).first
end
end
.
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %>
<%= f.email_field :email, autofocus: true %></div>
<div><%= f.label :username %>
<%= f.text_field :username%><div>
<div><%= f.label :first_name %>
<%= f.text_field :first_name %><div>
...
.
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def show_user
#user = current_user
render 'show'
end
def index
#user = User.all
end
def new
#user = User.new
render profiles_path
end
def create
#user = User.new(user_params)
if #user.save
redirect_to posts_path, notice: 'User successfully added.'
else
render profiles_path
end
end
def edit
end
def update
if #user.update(user_params)
redirect_to posts_path, notice: 'Updated user information successfully.'
else
render action: 'edit'
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:name, :first_name, :last_name, :email, :username)
end
end
.
class RegistrationsController < Devise::RegistrationsController
def update
new_params = params.require(:user).permit(:email,
:username, :current_password, :password,
:password_confirmation)
change_password = true
if params[:user][:password].blank?
params[:user].delete("password")
params[:user].delete("password_confirmation")
new_params = params.require(:user).permit(:email,
:username)
change_password = false
end
#user = User.find(current_user.id)
is_valid = false
if change_password
is_valid = #user.update_with_password(new_params)
else
#user.update_without_password(new_params)
end
if is_valid
set_flash_message :notice, :updated
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end end
end
Schema:
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
t.string "first_name"
t.string "last_name"
t.string "username"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
Add this to your ApplicationController
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :username
end
end
Source: https://github.com/plataformatec/devise#strong-parameters
Situation
I want to create new Category using form.
In new.html.erb everything is good:
<%= form_for #cat do |f| %>
<%= f.label :description %>
<%= f.text_field :description %>
<br>
<%= f.label :position %>
<%= f.text_field :position %>
<%= f.submit %>
<% end %>
But after "Submit" is pressed ArgumentError in CategoriesController#create is raised (Unknown key: description). http://prntscr.com/1fijdk
categories_controller.rb
class CategoriesController < ApplicationController
def index
#categories = Category.all
end
def new
#cat = Category.new
end
def create
#category = Category.find(params[:category])
redirect_to :categories
end
end
category.rb
class Category < ActiveRecord::Base
has_many :items
end
schema.rb
ActiveRecord::Schema.define(version: 20130715035836) do
create_table "categories", force: true do |t|
t.string "description"
t.integer "position"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "items", force: true do |t|
t.string "name"
t.float "price"
t.text "description"
t.integer "category_id"
t.datetime "created_at"
t.datetime "updated_at"
end
end
In Rails 3 everything works great, but in Rails 4 attr_accessible is not generated and I'm a little bit confused. Where is a problem?
Rails 4 does not use attr_accessible but strong_parameters to allow (or not) mass assignment.
Now this is handled by the controller and not by the model, you have to specify in your controller what are the permitted attributes...
See : http://guides.rubyonrails.org/action_controller_overview.html#strong-parameters to understand how it works and at https://github.com/rails/strong_parameters
Cheers