ActiveRecord::RecordNotFound in RelationshipsController#create - ruby-on-rails

I wanted to create twitter like followers and following thing.
in my view i have
<% if current_user.following?(#otheruser) %>
<%= render 'unfollow' %>
<% else %>
<%= render 'follow' %>
<% end %>
in _follow.html.erb
<%= form_for (#otheruser), url: createfollower_path(#otheruser) ,:class=>"form-horizontal",method: :post do |f| %>
<%= f.hidden_field :user_id, :value => #otheruser.id %>
<%= f.submit "Follow", class: "btn btn-primary" %>
<% end %>
in controller create action
def create
user = User.find(params[:user_id])
current_user.follow(user)
redirect_to followuser_url
end
in user.rb
has_many :followers, class_name: "Relationship" #-> users following you
has_many :following, class_name: "Relationship", foreign_key: :follower_id, foreign_key: :user_id
def follow(other_user)
relationships.create(user_id: other_user.id)
end
in relationship.rb
class Relationship < ActiveRecord::Base
belongs_to :user
belongs_to :follower, class_name: "User"
validates :user, :follower, presence: true
validates :user_id, uniqueness: { scope: :follower_id }
end
now when I am trying to submit the follow button it is showing error as
"Couldn't find User with 'id'=" and parameters are
{"utf8"=>"✓","authenticity_token"=>"681q5ft03+WRdqgHagh/gI1mV3uohwaEj1sF8zdTycUAN5yTiVMT/wGCV4tLPRVRRFRA+6mYSS1bXk2ormA/zw==",
"user"=>{"user_id"=>"7"},
"commit"=>"Follow",
"format"=>"7"}

You need to rewrite your controller's create action.
def create
user = User.find(params[:user][:user_id])
current_user.follow(user)
redirect_to followuser_url
end
Should worked !!!

Related

Rails nested attributes "no implicit conversion of Symbol into Integer" for has_many association

I've following are the Model codes.
user.rb
has_many :teams, dependent: :destroy
has_many :companies, dependent: :destroy
after_create :create_tables!
def create_tables!
companies.create!
Team.create!(user_id: self.id, company_id: Company.where(user_id: self.id).first.id)
end
company.rb
belongs_to :user
has_many :teams, inverse_of: :company, dependent: :destroy
has_many :users, through: :teams
accepts_nested_attributes_for :teams
team.rb
belongs_to :user
belongs_to :company, inverse_of: :teams
Following are my Controller codes
companies_controller.rb
def new
#company = current_user.companies.new
#company.build_teams
end
def update
current_user.companies.first.update_attributes(company_params)
respond_to {|format| format.js}
end
private
def company_params
params.require(:company).permit(:name, :about, :problem, :solution, :logo, :url, :email, :phone, :category, :started_in,
teams_attributes: [:position, :admin])
end
In views
<%= form_with model: #company, method: :put do |f| %>
<%= f.fields_for :teams_attributes, #company.teams.first do |team| %>
<%= team.hidden_field :admin, value: true %>
<%= team.text_field :position, placeholder: 'Eg: CEO', class: 'input' %>
<% end %>
<%= f.submit 'Next' %>
<% end %>
When i try this in rails console it works and saved in db, in views params are also passing good. Its below
But in views it says
TypeError in CompaniesController#update no implicit conversion of Symbol into Integer
It should be f.fields_for :teams instead of f.fields_for :teams_attributes
<%= form_with model: #company, method: :put do |f| %>
<%= f.fields_for :teams, #company.teams.first do |team| %>
<%= team.hidden_field :admin, value: true %>
<%= team.text_field :position, placeholder: 'Eg: CEO', class: 'input' %>
<% end %>
<%= f.submit 'Next' %>
<% end %>

Create controller method with accepts_nested_attributes_for and fields_for

i'm building a web application with Rails 5.2.0 about recipes and I have a doubt about the create method of the controller.
This are my models:
class Recipe < ApplicationRecord
belongs_to :user
has_many :quantities
has_many :ingredients, through: :quantities
accepts_nested_attributes_for :quantities, allow_destroy: true
end
class Quantity < ApplicationRecord
belongs_to :recipe
belongs_to :ingredient
end
class Ingredient < ApplicationRecord
has_many :quantities
has_many :recipes, through: :quantities
end
And here the view to create new recipes:
<%= form_for(#recipe) do |f| %>
<%= f.label :name, "Name" %>
<%= f.text_field :name %>
<%= f.label :servings, "Servings" %>
<%= f.number_field :servings %>
<%= f.fields_for :quantities do |quantity| %>
<%= f.hidden_field :_destroy, class: "hidden-field-to-destroy" %>
<%= f.label :ingredient_id, "Ingredient Name" %>
<%= f.text_field :ingredient_id%>
<%= f.label :amount, "Amount" %>
<%= f.number_field :amount %>
<%= f.label :unit, "Unit" %>
<%= f.select(:unit, ["kg","g","l","ml"], {include_blank: true}) %>
<% end %>
<%= f.submit 'Add new recipe' %>
<% end %>
I can add new ingredients dynamically with jquery and also delete them in the same form.
The update method of the controller works perfectly but the create method does not work:
class RecipesController < ApplicationController
def create
#recipe = current_user.recipes.build(recipe_params)
if #recipe.save
flash[:success] = "New recipe created correctly."
redirect_to #recipe
else
render 'new'
end
end
def update
#recipe = Recipe.find(params[:id])
if #recipe.update_attributes(recipe_params)
flash[:success] = "The recipe has been updated correctly."
redirect_to #recipe
else
render 'edit'
end
end
private
def recipe_params
params.require(:recipe).permit( :name, :servings, quantities_attributes: [:ingredient_id, :amount, :unit,:_destroy, :id, :recipe_id])
end
end
I'm trying to do #recipe = current_user.recipes.build(recipe_params) but I get the following error in te view:
Quantities recipe can't be blank
I think this occurs because when trying to create the relation, it is necessary to indicate the recipe_id, but the recipe has not yet been created and the id can not be indicated.
Could you please tell me someone what would be the correct way to create the recipe first and then be able to add the ingredients through Quantity in the create method of the recipe controller?
As per the message shared the qunatity_recipes cannot be blank and you haven't specified any condition to manage this.
Current
class Recipe < ApplicationRecord
belongs_to :user
has_many :quantities
has_many :ingredients, through: :quantities
accepts_nested_attributes_for :quantities, allow_destroy: true
end
Update the accepts nested attributes to allow_nil for Recipe class
class Recipe < ApplicationRecord
belongs_to :user
has_many :quantities
has_many :ingredients, through: :quantities
accepts_nested_attributes_for :quantities, allow_destroy: true, allow_nil: true
end

Rails nested form with cocoon. Attributes using model

I am trying to create a nested form which has options and suboptions, both from the same model called Option. Here is the content of the files:
Model:
class Option < ApplicationRecord
belongs_to :activity
has_many :option_students
has_many :students, through: :option_students
has_many :suboptions,
class_name: "Option",
foreign_key: "option_id"
belongs_to :parent,
class_name: "Option",
optional: true,
foreign_key: "option_id"
accepts_nested_attributes_for :suboptions,
reject_if: ->(attrs) { attrs['name'].blank? }
validates :name, presence: true
end
Controller:
class OptionsController < ApplicationController
include StrongParamsHolder
def index
#options = Option.where(option_id: nil)
end
def show
#option = Option.find(params[:id])
end
def new
#option = Option.new()
1.times { #option.suboptions.build}
end
def create
#option = Option.new(option_params)
if #option.save
redirect_to options_path
else
render :new
end
end
def edit
#option = Option.find(params[:id])
end
def update
#option = Option.find(params[:id])
if #option.update_attributes(option_params)
redirect_to options_path(#option.id)
else
render :edit
end
end
def destroy
#option = Option.find(params[:id])
#option.destroy
redirect_to options_path
end
end
_form.html.erb:
<%= form_for #option do |f| %>
<p>
<%= f.label :name %><br>
<%= f.text_field :name %><br>
<%= f.label :activity %><br>
<%= select_tag "option[activity_id]", options_for_select(activity_array) %><br>
</p>
<div>
<div id="suboptions">
<%= f.fields_for :suboptions do |suboption| %>
<%= render 'suboption_fields', f: suboption %>
<% end %>
<div class="links">
<%= link_to_add_association 'add suboption', f, :suboptions %>
</div>
</div>
</div>
<p>
<%= f.submit "Send" %>
</p>
<% end %>
_suboption_fields.html.erb
<div class="nested-fields">
<%= f.label :suboption %><br>
<%= f.text_field :name %>
<%= link_to_remove_association "X", f %>
</div>
StrongParamsHolder:
def option_params
params.require(:option).permit(:name, :activity_id, :students_ids => [], suboptions_attributes: [:id, :name])
end
The view is created correctly, but it is not saving. It goes to "render :new" on create controller. I think it should be a problem with the params, but I am not sure what.
Probably not saving because of a failed validation. If you are using rails 5, the belongs_to is now more strict, and to be able to save nested-params you need to make the connection/relation between association explicit.
So imho it will work if you add the inverse_of to your relations as follows:
has_many :suboptions,
class_name: "Option",
foreign_key: "option_id",
inverse_of: :parent
belongs_to :parent,
class_name: "Option",
optional: true,
foreign_key: "option_id"
inverse_of: :suboptions
If another validation is failing, it could also help to list the errors in your form (e.g. something like #option.errors.full_messages.inspect would help :)
As an aside: I would rename the option_id field in the database to parent_id as this more clearly conveys its meaning.

Follow user relationship

I am implementing a follow feature for my users. For this, I followed instruction of micheal hartl tutorial link needed, yet I am getting this error:
undefined method id' for nil:NilClass <% if current_user.following?(#user) %>
this is my user model
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
def following?(user)
relationships.find_by(followed_id: user.id)
end
def follow!(user)
relationships.create!(followed_id: user.id)
end
def unfollow!(user)
relationships.find_by(followed_id: user.id).destroy
end
this is my relationship model
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
end
and this is my view
<% if current_user.id && current_user.id != user.id %>
<div id="follow_form">
<% if current_user.following?(#user) %>
<%= form_for(current_user.relationships.find_by(followed_id: #user.id),
html: { method: :delete }) do |f| %>
<%= f.submit "Unfollow", class:"unfollow-button" %>
<% end %>
<% else %>
<%= form_for(current_user.relationships.build(followed_id: #user.id)) do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<%= f.submit "Follow", class:"follow-button" %>
<% end %>
<% end %>
this is the user controller
def show
#user= User.find_by_slug(params[:id])
if #user
#posts= Post.all
render action: :show
else
render file: 'public/404', status: 404, formats: [:html]
end
end
def index
#users = (current_user.blank? ? User.all : User.find(:all, :conditions => ["id != ?", current_user.id]))
end
This means #user is nil. Make sure that #user object is not null by doing
= debug #user
That should print all the details about #user object.

How to show name of user who wrote comment (Rails)?

In my rails app I have users(authors),posts(articles),comments. If registered user write comment to article, I want to show his name beside his comment, if he isn't registered user I want to show "Anonymous" beside his comment. How can I do this?
comment model:
class Comment < ActiveRecord::Base
attr_accessible :post_id, :text
belongs_to :post
belongs_to :user
end
user model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
has_many :posts, :dependent => :destroy
has_many :comments, :dependent => :destroy
validates :fullname, :presence => true, :uniqueness => true
validates :password, :presence => true
validates :email, :presence => true, :uniqueness => true
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :fullname
end
post model:
class Post < ActiveRecord::Base
attr_accessible :text, :title, :tag_list
acts_as_taggable
validates :user_id, :presence => true
validates :title, :presence => true
validates :text, :presence => true
belongs_to :user
has_many :comments
end
view file (show.html.erb)
<h1><%= #post.title %></h1>
<p>
Created: <%= #post.created_at.strftime("%Y/%m/%d")%> by
<%= link_to #post.user.fullname, user_posts_path(#post.user) %>
</p>
<p><%=simple_format #post.text %></p>
<p>
Tags: <%= raw #post.tag_list.map { |t| link_to t, tag_path(t) }.join(', ') %>
</p>
<h2>Comments</h2>
<% #post.comments.each do |comment| %>
<p><%= comment.created_at.strftime("%Y/%m/%d") %>
by <%= HERE I NEED ADD SOMETHING%></p>
<p><%= comment.text %></p>
<p><%= link_to "Delete comment", [#post, comment], :method => :delete,
:confirm =>"Are you sure?"%></p>
<% end %>
<%= form_for [#post, #post.comments.build] do |f| %>
<p><%= f.text_area :text %></p>
<p><%= f.submit "Post comment" %></p>
<% end %>
<% if user_signed_in?%>
<p>
<%= link_to "Back", posts_path %>
<%= link_to "Edit", edit_post_path(#post) %>
<%= link_to "Delete", #post, :method => :delete, :confirm => "Are you sure?"%>
</p>
<% end%>
You can do this by calling the user method on the comment, and then name on that:
<%= comment.user.name %>
You can also define a to_s method in the User model:
def to_s
name
end
Which would mean you could get away with doing just this in the view:
<%= comment.user %>
If you're loading a whole bunch of comments, then I would recommend loading them this way:
#comments = Comment.includes(:user)
If you don't have that includes(:user) there, then Rails will issue a new query for every single comment to find that comment's user. Doing it this way makes Rails load all the users for all the comments upfront in just one query.
I think you want to create two assocations pointing to the same table:
class CreatePostss < ActiveRecord::Migration
def change
create_table :posts do |t|
t.text :text
t.references :user, index: true, foreign_key: true
t.references :author, index: true, foreign_key: { to_table: :users }
t.timestamps null: false
end
end
end
Here user_id references the target user of the post and author_id references the user writing the post. Both reference users.id.
Then create two belongs_to associations in your post model:
class Post < ApplicationRecord
belongs_to :user
belongs_to :author,
class_name: 'User',
inverse_of: :authored_posts
end
And two has_many associations in your User model:
class User < ApplicationRecord
# posts about this user
has_many :posts
# postss written by this user
has_many :authored_posts,
class_name: 'Post',
foreign_key: :author_id,
inverse_of: :author
end
Here is the controller
class PostsController < ApplicationController
before_action :set_user, only: [:new, :create]
# ...
def new
#post = #user.posts.new
end
def create
#post = #user.posts.new(post_params) do |c|
c.author = current_user
end
if #post.save
redirect_to doctor_path(id: #user.id)
else
render :new
end
end
private
def set_user
#user = User.find(params[:id])
end
# ...
end
To display posts you would do:
<% #user.posts.each do |post| %>
<div class="post">
<div class="body">
<%= post.body %>
</div>
<p class="author"><%= post.author.email %></p>
</div>
<% end %>

Resources