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'm a little stuck and any help or advise would be much appreciated.
I would like to display only users that have a resume but I am unsure how
- I have the below codings in my file - your advise & help will be much appreciated.
in my user/index.html
<% users.each do |user| %>
<%= user.resume.summary %>
<% end %>
schema
create_table "resumes", force: true do |t|
t.text "summary"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
models
user.rb
has_one :resume
resume.rb
belongs_to :user
I would like to do a search that only displays users with a resume.
I tried the below but no luck
<% users.where(resume_id: !nil).each do |user| %>
<%= user.resume.summary %>
<% end %>
<% users.where("resume IS NOT NULL != ?", "").each do |user| %>
<%= user.resume.summary %>
<% end %>
I tried writing as a scope but no luck
user.rb file
scope :users_with_resumes, -> {where(['resume != ?', nil])}
users/index.html.erb file
<% #users.users_with_resumes.each do |userj| %>
<%= user.resume.summary %>
<% end %>
You're close to hitting the mark, but your syntax in your attempts is slightly off:
# This won't work because you're now searching for the value !nil (or 'true')
<% users.where(resume_id: !nil).each do |user| %>
<%= user.resume.summary %>
<% end %>
# This is a double negative, syntactically incorrect, and referencing resume instead of resume_id
<% users.where("resume IS NOT NULL != ?", "").each do |user| %>
<%= user.resume.summary %>
<% end %>
You should instead use one of the following:
users.where.not(resume_id: nil)
users.where("resume_id IS NOT NULL")
I think you'll find either of those statements will serve your purpose whether scoped or otherwise.
The below code displays only users with resumes
Association
user has_one resume
resume belongs_to user
schema
create_table "resumes", force: true do |t|
t.text "summary"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
user.rb
scope :users_with_resumes, -> {where("id in (select user_id from resumes)")}
or instead of a scope you can write a method
def self.users_with_resumes
where("id in (select user_id from resumes)")
end
users/index.html.erb [this display only users with a resume]
<% #users.users_with_resumes.each do |user| %>
<%= user.resume.summary %>
<% end %>
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'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
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.