Ruby on rails NoMethodError in controller after create an association - ruby-on-rails

I have two models User and Promotion, an user can create has_many promotion and an promotion belong to user so :
promotion.rb
class Promotion < ActiveRecord::Base
belongs_to :user
belongs_to :good
validates :name, :presence => true
validates :title, :presence => true
validates :description, :presence => true
end
for the users i used devise so:
user.rb
class User < ActiveRecord::Base
has_many :promotions ,:foreign_key => "user_id",
:dependent => :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => [:facebook]
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me,:provider,:uid,:address,:name,:surname,:supplier,:partita_iva,:state,
:gender ,:language,:bio,:work,:education
now when i want create a new promotions get this error
NoMethodError in PromotionsController#create
undefined method `promotions' for nil:NilClass
this is the controller:
def create
#user = User.find_by_id(params[:user_id])
#promotion =#user.promotions.create(:params[:promotion])
redirect_to promotion_patch(#promotion)
respond_to do |format|
if #promotion.save
format.html { redirect_to #promotion, notice: 'Promotion was successfully created.' }
format.json { render json: #promotion, status: :created, location: #promotion }
else
format.html { render action: "new" }
format.json { render json: #promotion.errors, status: :unprocessable_entity }
end
end
end
help please :)

It looks as though params[:user_id] did not contain a valid user id. Since you used find_by_id instead of find, it quietly assigned nil to #user, and of course nil doesn't have a method named #promotions, so that line failed.
You need to either check for #user being nil, or change User.find_by_id to User.find and then rescue ActiveRecord::RecordNotFound. In either case, respond with a custom 404 or whatever other way seems appropriate.
One other question, is it your intention that a user can create promotions for any other user? If they should only be creating promotions for themselves, you can avoid this whole mess by just eliminating the whole User.find_by_id line, and changing the next line to:
#promotion = current_user.promotions.create(params[:promotion])
Devise should have already current_user for you. In any case, you also need to handle what happens if the promotion cannot be created because there are validation errors in the user-supplied parameters.

Related

Undefined method error with has_one association

I have a Shop model and a User model. Logic is that users can have one shop and shops can have one users associated with them, so I am using the has_one: shop association.
But while creating a new shop for a new user am getting this error
undefined method 'shops' for #<\User:0x007f6f30659068> Did you mean? shop shop=
I don't understand what's gone wrong. I am sure I must have done something stupid, here's my code:
class ShopsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
def new
#shop = current_user.shop.build
end
def create
#shop = current_user.shops.build(shop_params)
#shop.user = current_user
respond_to do |format|
if #shop.save
format.html { redirect_to #shop, notice: 'Shop was successfully created.' }
format.json { render :show, status: :created, location: #shop }
else
format.html { render :new }
format.json { render json: #shop.errors, status: :unprocessable_entity }
end
end
end
private
def shop_params
params.require(:shop).permit(:name, :description, :imageshop, :location, :web, :email, :phone, :business_type, :category)
end
end
class Shop < ApplicationRecord
mount_uploader :imageshop, ImageUploader
belongs_to :user
end
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :products, dependent: :destroy
has_one :shop
end
I think you need to change
#shop = current_user.shops.build(post_params)
to
#shop = current_user.build_shop(post_params)
This is because you have specified that a User can have one Shop.
Hope it helps!

Rails comments and username won't show up in post views / undefined method `user' for nil:NilClass

So I have a post.rb model where I can display who wrote the post in the show post view, and that works wonderfully.
However, I've been trying to add comments, which belong to a post, and also belong to a user.
I can't seem to get all the comments or the names of the users who posted the coments to show up in the post show.html.erb.
I guess my question is, how do I integrate controller and model information in between controller and model views? I know if it exists in the model and controller and view of the same type, it is accessible, but cross-linking or sharing controller model information is hard for me.
I want to be able to display the user that made the comment, along with the body of the comment in the show view of the post, and not the comment.
comment.rb
class Comment < ApplicationRecord
belongs_to :post
belongs_to :user
validates :body, :presence => true
end
post.rb
class Post < ApplicationRecord
belongs_to :user, optional: true
validates :user, presence: true;
validates :title, presence: true,
length: { minimum: 5}
extend FriendlyId
friendly_id :title, use: :slugged
validates :title, :slug, presence: true
# comments
has_many :comments
end
user.rb
class User < ApplicationRecord
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates :first_name, presence: true
validates :last_name, presence: true
validates :school_name, presence: true, inclusion: { in: %w(Harvard Yale),
message: "%{value} is not a valid choice" }
validates :graduation_year, presence: true, inclusion: { in: %w(2017 2018 2019 2020 2021 2022 2023),
message: "%{value} is not a valid choice currently" }
def superadmin?
self.role.name == "Superadmin"
end
end
comments_controller.rb
class CommentsController < ApplicationController
before_action :authenticate_user!
# GET /posts/:post_id/comments
# GET /posts/:post_id/comments.xml
def index
#1st you retrieve the post thanks to params[:post_id]
post = Post.friendly.find(params[:post_id])
#2nd you get all the comments of this post
#comments = post.comments
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #comments }
end
end
# GET /posts/:post_id/comments/:id
# GET /comments/:id.xml
def show
#1st you retrieve the post thanks to params[:post_id]
post = Post.friendly.find(params[:post_id])
#2nd you retrieve the comment thanks to params[:id]
#comment = post.comments.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #comment }
end
end
# GET /posts/:post_id/comments/new
# GET /posts/:post_id/comments/new.xml
def new
#1st you retrieve the post thanks to params[:post_id]
post = Post.friendly.find(params[:post_id])
#2nd you build a new one
#comment = post.comments.new(params[:comment])
#comment.user = current_user
end
# GET /posts/:post_id/comments/:id/edit
def edit
#1st you retrieve the post thanks to params[:post_id]
post = Post.friendly.find(params[:post_id])
#2nd you retrieve the comment thanks to params[:id]
#comment = post.comments.find(comment_params[:id])
end
# POST /posts/:post_id/comments
# POST /posts/:post_id/comments.xml
def create
#1st you retrieve the post thanks to params[:post_id]
post = Post.friendly.find(params[:post_id])
#2nd you create the comment with arguments in params[:comment]
#comment = post.comments.create(comment_params)
#comment.user = current_user
respond_to do |format|
if #comment.save
#1st argument of redirect_to is an array, in order to build the correct route to the nested resource comment
format.html { redirect_to([#comment.post, #comment], :notice => 'Comment was successfully created.') }
#the key :location is associated to an array in order to build the correct route to the nested resource comment
format.xml { render :xml => #comment, :status => :created, :location => [#comment.post, #comment] }
else
format.html { render :action => "new" }
format.xml { render :xml => #comment.errors, :status => :unprocessable_entity }
end
end
end
In the show method of your post_controller, you'll need to do something like:
def show
#post = Post.find_by(id: params[:id]) # or however you find your post
....
end
Then, in your views/posts/_show.html.erb (if you're using erb), you'll do something like:
<% #post.comments.each do |comment| %>
... show some comment stuff
<%= comment.user.name %>
<% end %>

Devise Validations with Associations

I have a User model generated using Devise. This User model has 2 types (Buyer or Seller) created using Polymorphic Associations.
class Buyer < ActiveRecord::Base
has_one :user, as: :owner, dependent: :destroy
accepts_nested_attributes_for :user
validates_presence_of :user
validates_associated :user
class User < ActiveRecord::Base
after_commit :transaction_success
after_rollback :transaction_failed
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :owner, polymorphic: true
validates :email, presence: true
validates :first_name, presence: true
private
def transaction_success
logger.info "Transfer succeed for Account #{self.to_param}"
end
def transaction_failed
logger.warn "Transfer failed for Account #{self.to_param}"
end
end
When an User is Registered, he does it from an specific registration link (/buyer/new or /seller/new), calling the create method from the controller:
def create
#buyer = Buyer.new(buyer_params)
#user = User.new(user_params)
respond_to do |format|
begin
User.transaction do
#buyer.save
#user.owner_id = #buyer.id
#user.owner_type = "Buyer"
#user.save
end
format.html { sign_in_and_redirect #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
rescue ActiveRecord::Rollback
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
The problem is that sometimes the transaction fails but saves a Buyer without an User. Also, the Devise validations, or any validation that I include in the User Model doesn't make any difference, allowing users to be created without any email, password etc.
How can I fix the transaction and make sure that the validations work??

Devise: Can't mass-assign protected attributes: email, name

I'm using Rails (3.2.3) and Devise, and allowing administrators to create new users – and edit user accounts.
At this point, administrators can create accounts successfully. However, they can't edit them.
When you try to edit a user's account, a mass-assignment error is raised:
Can't mass-assign protected attributes: email, name
Even though, in the User model, these attributes are set to accessible:
attr_accessible :name, :email, :password, :password_confirmation, :remember_me
What's interesting, is, if I change the above line to attr_protected, you CAN edit user information but you CANNOT create users anymore. Very weird.
Here's the relevant code I'm working with... any help is appreciated.
User model:
class User < ActiveRecord::Base
rolify
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :role_ids, :as => :admin
attr_accessible :name, :email, :password, :password_confirmation, :remember_me
end
Users controller:
class UsersController < ApplicationController
before_filter :authenticate_user!
def update
authorize! :update, #user, :message => 'Not authorized as an administrator.'
#user = User.find(params[:id])
if params[:user][:password].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
if #user.update_attributes(params[:user], :as => :admin)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def new
#user = User.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
end
def edit
#user = User.find(params[:id])
end
def create
#user = User.new(params[:user])
if params[:user][:password].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
respond_to do |format|
if #user.save
format.html { redirect_to users_path, notice: 'User was successfully created.' }
else
format.html { render action: "new" }
end
end
end
end
Change to
class User < ActiveRecord::Base
rolify
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :role_ids, :name, :email, :password, :password_confirmation, :remember_me, :as => :admin
attr_accessible :name, :email, :password, :password_confirmation, :remember_me
end
Now administrators can create accounts successfully & they CAN edit them as well. Hope it helps

accepts_nested_attributes_for giving error: Can't mass-assign protected attribute

I'm getting 'Can't mass-assign protected attribute' error when trying to save a form that uses 'accepts_nested_attributes_for'. I think I have code the model correctly but not sure what I missed. Any idea?
error: Can't mass-assign protected attributes: organization
user.rb
class User < ActiveRecord::Base
belongs_to :organization
accepts_nested_attributes_for :organization
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me,
:username, :first_name, :last_name, :organization_attributes
end
organization.rb
class Organization < ActiveRecord::Base
has_many :users
attr_accessible :address1, :address2, :city, :country, :fax, :name, :phone, :state, :zip
end
users_controller.rb
def new
#user = User.new
#organization = Organization.new
respond_to do |format|
format.html # new.html.erb
end
end
def create
#user = User.new(params[:user])
#organization = #user.organizations.build(params[:organization])
respond_to do |format|
if #user.save
#organization.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
else
format.html { render action: "new" }
end
end
end
Manage to get what I want based on the answer on this question: Use both Account and User tables with Devise
By the way I'm also using STI and it works great.

Resources