I just want to show the username who created the events. But when I try it says
undefined method `user_name' for nil:NilClass
This is my event.rb
class Event < ActiveRecord::Base
belongs_to :user
validates :name, presence: true
validates :description, presence: true, length: {minimum: 5}
end
And this is my user.rb
class User < ActiveRecord::Base
has_secure_password
has_many :events
end
And I am trying to show the user name in html.erb file like this.
<%= event.user.user_name %>
But I am getting this error.
So this is my create method in events_controller
def create
#event = current_user.events.new(event_params)
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Event was successfully created.' }
else
format.html { render :new }
end
end
end
So what should I do for showing username in that page.
Thank You
Ok so here's the problem:
You want to show the event's user's name in index page but you can't be sure all events have one user associated with them. To avoid it you could use.
<%= event.user.try(:user_name) || 'No user associated' %>
Good luck!
Although the answer will get your app to work, it won't fix your problem.
The core issue is that your Event doesn't have an associated User object. This would not be a problem, except it seems from your code that you require an Event to have an associated User (current_user.events.new etc)...
You need the following:
#app/models/event.rb
class Event < ActiveRecord::Base
belongs_to :user
validates :user, presence: true #-> always makes sure user exists
delegate :user_name, to: :user #-> event.user_name
end
--
If you use the above code, you'll be able to call #event.user_name (solving the law of demeter with delegate). You'll also benefit from using validates presence to ensure the :user association exists on create & update.
This will allow you to rely on the #event.user object, which - to me - is far better than having to say "No user associated" in your app:
#view
<%= #event.user_name if #event.user %>
Related
I'm trying to learn Ruby on Rails, an I'm kinda stuck with associaton.
My project is to create a simple blog with three table. User, Post, and Comment.
In my understanding, after associationg several table with foreign key, rails would automatcily find user_id and post_id. But everytime I try to build comments, the user_id is nil.
Here's my model:
class User < ApplicationRecord
has_many :posts
has_many :comments
validates :name, presence: true, length: { minimum: 5 }, uniqueness: true
validates :password, presence: true, length: { minimum: 5 }
end
class Post < ApplicationRecord
belongs_to :user
has_many :comments
validates :title, presence: true
validates :body, presence: true, length: {minimum: 10}
end
class Comment < ApplicationRecord
belongs_to :post
belongs_to :user
validates :body, presence: true
validates :user_id, presence: true
validates :post_id, presence: true
end
Here is the screenshot when I try to create a comment:
As you can see, the post_id is not nil but the user_id is nil.
I try to input user_id manualy and it work as intended. But I can't find out how to create comment with automatic user_id and post_id.
In my understanding, after associationg several table with foreign key, rails would automatcily find user_id and post_id. But everytime I try to build comments, the user_id is nil.
There is no truth to that assumption. Rails will not automatically assign your assocations - how should it even know what user/post you want to associate the comment with?
Typically the way you would construct this is to have a nested route:
resources :posts do
resources :comments,
only: [:create]
shallow: true
end
This creates the route /posts/:post_id/comments so that we know which post the user wants to comment on - you would then adjust your forms so that it posts to the nested route:
# app/views/comments/_form.html.erb
<%= form_with(model: [post, comment]) do |f| %>
# ...
<% end %>
# app/views/posts/show.html.erb
# ....
<h2>Leave a comment</h2>
<%= render partial: 'comments/form',
locals: {
post: #post,
comment: #comment || #post.comments.new
}
%>
Getting the user who's commenting would typically be done by getting it from the session through your authentication system - in this example the authenticate_user! callback from Devise would authenticate the user and otherwise redirect to the sign in if no user is signed in.
You then simply assign the whitelisted parameters from the request body (from the form) and the user from the session:
class CommentsController
before_action :authenticate_user!
# POST /posts/1/comments
def create
# This gets the post from our nested route
#post = Post.find(params[:post_id])
#comment = #post.comments.new(comment_params) do |c|
c.user = current_user
end
if #comment.save
redirect_to #post,
status: :created
notice: 'Comment created'
else
render 'comments/show',
status: :unprocessable_entity,
notice: 'Could not create comment'
end
end
private
def comment_params
params.require(:comment)
.permit(:foo, :bar, :baz)
end
end
This is typically the part that Rails beginners struggle the most with in "Blorgh" tutorials as it introduces a resource thats "embedded" in another resource and its views and several advanced concepts. If you haven't already I read it would really recommend the Getting Started With Rails Guide.
you can create a comments as below:
user = User.find 2
post = user.posts.where(id: 2).first
comment = post.comments.build({comment_params}.merge(user_id: user.id))
Hope this will help you.
I want to make it so that users who log into my site can only like once on a question, But I would also want people who aren't logged in to also be able to like.
currently I have this to ensure that logged in users only vote once
model
class Yesvote < ActiveRecord::Base
belongs_to :user
belongs_to :question
validates_uniqueness_of :user, scope: :question
end
controller
def yesvote
#question = Question.find(params[:id])
if current_user != nil
yesvote = Yesvote.create(yes: params[:yes], user: current_user, question: #question)
else
yesvote = Yesvote.create(yes: params[:yes], question: #question)
end
if yesvote.valid?
redirect_to :back
else
flash[:danger] = "once only!"
redirect_to :back
end
end
currently if one user likes without logging in, it prevents further likes from un-logged in users. basically, it prevents more than one yesvotes to have a user_id of null/nil
This may helpful :-
validates_uniqueness_of :user_id, :allow_blank => true, :scope => [:question_id]
:allow_blank or :allow_nil, which will skip the validations on blank and nil fields, respectively.
To validate multiple attributes you could use scope:
class Yesvote < ActiveRecord::Base
belongs_to :user
belongs_to :question
validates_uniqueness_of :user_id, scope: :question_id
end
I guess you are using devise for authentication. If so, you can add a before filter in your controller to authenticate users before voting:
before_filter: authenticate_user!, only: :yesvote
def yesvote
#question = Question.find(params[:id])
yesvote = Yesvote.create(yes: params[:yes], user: current_user, question: #question)
redirect_to :back
end
Edit:
You can use Proc to skip validation if user_id is blank.
validates_uniqueness_of :user_id, scope: :question_id, unless: Proc.new { |v| v.user_id.blank? }
I'm creating a simple newsfeed in rails. The aim is for it to return all the posts from the groups the user is following. I am using socialization for my follow functionality.
The exact error is:
NoMethodError (undefined method `followees' for false:FalseClass)
Here are my basic models not including like and follow as they're empty:
User:
class User < ActiveRecord::Base
authenticates_with_sorcery!
attr_accessible :username, :password, :email
has_many :groups
has_many :posts
acts_as_follower
acts_as_liker
before_create :generate_auth_token
def auth_token_expired?
auth_token_expires_at < Time.now
end
def generate_auth_token(expires = nil)
self.auth_token = SecureRandom.hex(20)
self.auth_token_expires_at = expires || 1.day.from_now
end
def regenerate_auth_token!(expires = nil)
Rails.logger.info "Regenerating user auth_token"
Rails.logger.info " Expiration: #{expires}" if expires
generate_auth_token(expires)
save!
end
end
Group:
class Group < ActiveRecord::Base
attr_accessible :description, :name, :user_id
has_many :posts
belongs_to :user
acts_as_followable
end
Post:
class Post < ActiveRecord::Base
attr_accessible :body, :user_id, :group_id
belongs_to :user
belongs_to :group
acts_as_likeable
end
I have setup a function named newsfeed in my post controller. The function grabs all the groups that a user is following and then grabs all the posts that have group_ids matching group_ids in the returned groups array. But I keep getting unidentified method followees(socialization provides this). Yet it appears to work when using single users and posts in irb.
def newsfeed
#groups = current_user.followees(Group)
#posts = Post.where(:group_id => #groups)
respond_to do |format|
format.html # show.html.erb
format.json { render json: #posts }
end
end
Thanks for any help.
Apparently, your current_user method returns false, instead of a user. Check what's returned from that method, as find out why you get the error...
Your current_user return false instead of instance of User. You may see it from error text.
I'm stumped on my Rails app. I've created 3 models: user, event, category that have the following associations:
class User
has_many :events, :dependent => :destroy
attr_accessible :name, :email, :password, :password_confirmation, :remember_me
class Category
attr_accessible :name
has_many :events
class Event
attr_accessible :address, :cost, :date, :details, :end_time, :fav, :start_time, :title, :venue
belongs_to :user
belongs_to :category, :foreign_key => :name #not sure if this foreign key is working
The idea is that Users create events that are filed under a single category. The categories are pre-populated so users don't get to make them, only choose which to file under.
My user and events association has been working for a while, but since I've added the category model I get a "Can't mass-assign protected attributes: category" error when I try to create a new event.
I've been browsing pages all day and can't seem to track the error down. I tried adding a foreign key using belongs_to :category, :foreign_key => :name in the Event class but that didn't seem to help.
Would graciously appreciate any help, solutions, and/or pointers in the right direction!
Edit 2: I'm pretty new at Rails, but I think I've tracked down where the problem is from the error screen. Says "ActiveModel::MassAssignmentSecurity::Error in EventsController#create" and further down says "app/controllers/events_controller.rb:58:in create" which equates to this line of code: #event = current_user.events.new(params[:event]).
If I'm reading it correctly, that would mean the error occurs because I'm trying to create a new event with a category param passed in the hash and it doesn't know what to do with it. Unfortunately, I don't know what to do either...
Edit 3: As requested, here's the Event controller's create action:
def create
#event = current_user.events.new(params[:event])
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Event was successfully created.' }
format.json { render json: #event, status: :created, location: #event }
else
format.html { render action: "new" }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
Place the :foreign_key => :name in class User OR provide attr_accessible to class User
Add :category to attr_accessible in Event model.
And using name as foreign key is a bad idea, becouse it isn't unique
For create event with relation with user and category you can do:
user.events.build do |event|
event.category = category
end
or
category.events.build do |event|
event.user =user
end
Also, you can use method create
I have two models: The Discount has and belongs to many Businsses.
I want to validate that a Discount always have at least one business, together with another condition (for example active?). I tried the following:
class Discount < ActiveRecord::Base
has_and_belongs_to_many :businesses,
before_remove: :validate_publish_status
def validate_publish_status(*arg)
if active? && businesses.count == 0
errors[:active] << 'discount with no business'
end
end
end
However this does not work (no validation errors raised) and I realized that this is probably because it is only a callback, not a validation. How can I code it so I can use the errors like I do I custom validation?
The controller action I have (for ajax):
def remove
#business = Business.find(params[:business_id])
if #business.in? #discount.businesses
#discount.businesses.delete(#business)
end
render json: #business.as_json(only: [:id, :type, :name, :address],
methods: [:city_name, :country_name]).
merge(paths: paths_for(#discount, #business))
rescue ActiveRecord::RecordInvalid # even tried the generic Exception
respond_to do |f|
f.json { render json: {error: $!.message}, status: 403 }
end
end
Could be your syntax on the before_remove callback or what's happening in the validation method itself. You also might want to add some debug code in the callback method to see if it's even making in there.
*Note that the transaction will only be stopped if an exception is raised in the callback method. Since this is the case, you'll probably want to handle the exception logic in your controller to re-render the action:
class Discount < ActiveRecord::Base
has_and_belongs_to_many :businesses, :before_remove => :validate_publish_status
def validate_publish_status(*args)
if yyy? && businesses.count == 0
errors.add(:yyy,'discount with no business')
raise "Unable to remove business."
end
end
end
controller gist:
def update
#company.find(params[:id])
if #company.update_attributes(params[:company])
...
else
render :action => 'edit'
end
rescue
render :action=>'edit'
end
Note the Association Callback Documentation.
You can use the validates method with :presence => true for this.
class Discount < ActiveRecord::Base
has_and_belongs_to_many :businesses
validates :businesses, :presence => true
end
Using :presence => true on an association validator will ensure that at least one member of the association exists.