form not appearing for triple nested resource - ruby-on-rails

I know that resources should only be nested once, but I have two models that dip into the triple nested waters, which makes things more complex than they need to be, but I do not see a way to avoid it in these two cases...so:
The nesting works like this: group>navbar>links
I am having issues getting the link form to render:
- simple_form_for new_group_navbar_link_path(#group, #navbar, #link) do |f|
%fieldset.well.pleft80.edit
= f.input :method_name
= f.input :text
= f.input :button
.form-actions
= f.submit nil, :class => 'btn btn-primary pull-right btn-large'
navbar belongs_to groups and link belongs_to navbar
Controller:
class LinksController < ApplicationController
before_filter :fetch_group
before_filter :fetch_navbar
before_filter :fetch_link, only: [:show, :edit, :update, :destroy]
def show
end
def new
#link = Link.new
end
def create
#link = #navbar.links.build(params[:link])
if #link.save
redirect_to #navbar, notice: 'link was successfully updated.'
else
render :new
end
end
def edit
#image = #link.build_image unless #link.image
end
def update
respond_to do |format|
if #link.update_attributes(params[:link])
format.html { redirect_to #navbar, notice: 'link was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #link.errors, status: :unprocessable_entity }
end
end
end
def destroy
#link.destroy
redirect_to navbar_path(#navbar)
end
private
def fetch_group
#group = Group.find(params[:group_id])
end
def fetch_navbar
#navbar = Navbar.find(params[:navbar_id])
end
def fetch_link
#link = #navbar.links.find(params[:id])
end
end
Is there something simple I'm overlooking?

just replace
- simple_form_for new_group_navbar_link_path(#group, #navbar, #link) do |f|
with
= simple_form_for new_group_navbar_link_path(#group, #navbar, #link) do |f|

Related

No route matches {:action=>"create", :controller=>"reviews", :id=>"2", :user_id=>#<Profile id: 2...} missing required keys: [:profile_id]

I am working on user_profile_reviews and have got stuck. I have 3 models for now, and I know, that doing a separate model for a profile wasn't really a great idea, but since all my routes depend on this structure, meaning the links in all the views also, I decided not to change it.
To give you a clearer understanding:
Rails.application.routes.draw do
devise_for :user, controllers: { omniauth_callbacks: 'users/omniauth_callbacks', registrations: "users/registrations" }
resources :users do
resources :profiles do
resources :reviews, only: [:new, :create]
end
end
root 'home#index'
end
Here are my controllers:
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
def index
#profiles = Profile.all
end
def show
#user = User.find(params[:user_id])
#profile = Profile.find(params[:id])
#reviews = Review.where("profile_id = ?", params[:id])
end
def new
#user = User.find(params[:user_id])
end
def edit
#profile = Profile.find(params[:id])
end
def create
#profile = current_user.build_profile(profile_params)
respond_to do |format|
if #profile.save
format.html { redirect_to user_profile_path(current_user.id, current_user.profile.id), notice: 'Profile was successfully created.' }
format.json { render :show, status: :created, location: #profile }
else
format.html { render :new }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to user_profile_path(current_user.id, current_user.profile.id), notice: 'Profile was successfully updated.' }
format.json { render :show, status: :ok, location: #profile }
else
format.html { render :edit }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
def destroy
#profile.destroy
respond_to do |format|
format.html { redirect_to profiles_url, notice: 'Profile was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_profile
#profile = Profile.find(params[:id])
end
def profile_params
params.permit(:about, :rating, :avatar)
end
end
Reviews
class ReviewsController < ApplicationController
before_filter :set_profile
before_filter :set_review, only: [:new, :create]
def new
#review = Review.new
end
def create
#profile = Profile.find(params[:profile_id])
#review = #profile.reviews.build(review_params)
#review.user_id = current_user.id
if #review.save
redirect_to #profile
else
redirect_to #profile, notice: "Error saving"
end
end
private
def review_params
params.permit(:content, :rating)
end
def set_pfofile
#profile = Profile.find(params[:profile_id])
end
def set_review
#profile = Profile.find(params[:id])
end
end
So now, I am trying to create a form for reviews, which I am then rendering in Profiles#show, and getting the mistake above.
<div class="submit-review">
<%= form_for [#review, :url => user_profile_reviews_path(#profile)] do |f| %>
<label for="review">How was your experience?</label><br>
<%= f.label :rating %>
<%= f.select :rating, options_for_select([["Please select one", ""], 5, 4, 3, 2, 1]) %>
<%= f.input :content, placeholder:"Please enter your feedback here" %>
<%= f.submit "Submit your review", class: "btn btn-default" %> <br><br>
<% end %>
Showing ... /_form.html.erb where line #2 raised:
No route matches {:action=>"create", :controller=>"reviews", :id=>"2", :user_id=>#<Profile id: 2, about: "lena", rating: 3, created_at: "2019-11-22 21:27:03", updated_at: "2019-11-22 21:27:03", user_id: 2>}, missing required keys: [:profile_id]
But, as I see, it gets to the profile, I am onto, so I don't understand what's the issue here.
Something wrong with the syntax, try this
= form_for([#profile.user, #profile, #review], :url => user_profile_reviews_path(#profile.user, #profile)) do |f|
Since your resources are nested, you need to pass user, profile and then review as the first argument in form_for
Suggestion: Looking at your code, you don't even need user_id, you can avoid nesting profile and review under user in routes.
Hope that helps!
Ok, this worked perfectly for solving the described problem with missing id.
form_for([#profile.user, #profile, #review], :url => user_profile_reviews_path(#profile.user, #profile)) do |f|
I was getting another error though:
First argument in form cannot contain nil or be empty
I saw then, that in my Profile#show I wasn't defining #review. Only reviews. So I did it this way:
def show
#user = User.find(params[:user_id])
#profile = Profile.find(params[:id])
#review = Review.new
#reviews = Review.where("profile_id = ?", params[:id])
end
I can now finally go to profile and there is a review window, which is great! I can't save the reviews though, as another error is showing up. But that's different case. Thank you so much!

Simple search in the database

I have a problem with a simple browser in my application.
I have already looked for solutions but still displays the following error:
param is missing or the value is empty: expense.
Please help.
Index:
<%= form_tag expenses_path :method => 'get' do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search" , name: nil %>
Controller:
class ExpensesController < ApplicationController
before_action :set_expense, only: [:show, :edit, :update, :destroy]
# GET /expense expense
# GET /expense.json
def index
#expenses = Expense.search(params[:search])
end
# GET /expense/1
# GET /expense/1.json
def show
end
# GET /expense/new
def new
#expense = Expense.new
end
# GET /expense/1/edit
def edit
end
# POST /expense
# POST /expense.json
def create
#expense = Expense.new(expense_params)
respond_to do |format|
if #expense.save
format.html { redirect_to #expense, notice: 'zostały zapisane.' }
format.json { render :show, status: :created, location: #expense }
else
format.html { render :new }
format.json { render json: #expense.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /expense/1
# PATCH/PUT /expense/1.json
def update
respond_to do |format|
if #expense.update(expense_params)
format.html { redirect_to #expense, notice: 'expense was successfully updated.' }
format.json { render :show, status: :ok, location: #expense }
else
format.html { render :edit }
format.json { render json: #expense.errors, status: :unprocessable_entity }
end
end
end
# DELETE /expense/1
# DELETE /expense/1.json
def destroy
#expense.destroy
respond_to do |format|
format.html { redirect_to #expense, notice: 'Zakupy zostały usunięte.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_expense
#expense = Expense.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def expense_params
params.require(:expense).permit(:date, :price, :category, :where)
end
Expense.rb:
class Expense < ApplicationRecord
def self.search(search)
if search
where (['where LIKE ?',"%#{search}%"])
else
all
end
end
permit the search in your params
def expense_params
params.require(:expense).permit(:date, :price, :category, :where, :search)
end
please correct the form_tag syntax
<%= form_tag(expenses_path,method: :get) do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search" , name: nil %>
<% end %>
The error message you report is most likely coming from your expense_params method:
params.require(:expense) means you expect the parameters to be of the form params[:expense][:date], params[:expense][:price], etc.
My best guess is that your form is not actually submitting a GET request and so it's going to the create method rather than the index method.
I think this may be occurring because you are missing a comma between expenses_path and :method => 'get' in your form_tag. It should be:
<%= form_tag expenses_path, :method => 'get' do %>

Couldn't find ProjectSession with 'id'=

Here i have a project to which i am adding a session and for a project session i am trying to add task.
i am able to create project and add project session for project but when i am trying to add task for session using project_sessions_id i am getting error Couldn't find ProjectSession with 'id'= and 'app/controllers/tasks_controller.rb:60:in set_project_session i am able to get the project session id also project_sessions/11 in the url but when i click 'create task' i am getting this error. how can i resolve this?
here's what i have done
ProjectSessionController:
class ProjectSessionsController < ApplicationController
before_action :set_project_session, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
before_action :set_project
def index
#project_sessions = ProjectSession.all
end
def show
#project_sessions = ProjectSession.where(project_id: #project.id).order("created_at DESC")
end
def new
#project_session = ProjectSession.new
end
def edit
end
def create
#project_session = ProjectSession.new(project_session_params)
#project_session.project_id = #project.id
#respond_to do |format|
if #project_session.save
redirect_to #project
#format.html { redirect_to #project_session, notice: 'Project session was successfully created.' }
#format.json { render :show, status: :created, location: #project_session }
else
format.html { render :new }
format.json { render json: #project_session.errors, status: :unprocessable_entity }
end
#end
end
def update
respond_to do |format|
if #project_session.update(project_session_params)
format.html { redirect_to #project_session, notice: 'Project session was successfully updated.' }
format.json { render :show, status: :ok, location: #project_session }
else
format.html { render :edit }
format.json { render json: #project_session.errors, status: :unprocessable_entity }
end
end
end
def destroy
#project_session.destroy
respond_to do |format|
format.html { redirect_to project_sessions_url, notice: 'Project session was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_project_session
#project_session = ProjectSession.find(params[:id])
end
def set_project
#project = Project.find(params[:project_id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def project_session_params
params.require(:project_session).permit(:session_date, :session_name, :session_description, :start_time, :end_time)
end
end
Task controller:
class TasksController < ApplicationController
before_action :set_task, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
before_action :set_project_session
def index
#tasks = Task.all
end
def show
end
def new
#task = Task.new
end
def edit
end
def create
#task = Task.new(task_params)
#task.session_id = #project_session.id
respond_to do |format|
if #task.save
redirect_to #project_session
else
format.html { render :new }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #task.update(task_params)
format.html { redirect_to #task, notice: 'Task was successfully updated.' }
format.json { render :show, status: :ok, location: #task }
else
format.html { render :edit }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
def destroy
#task.destroy
respond_to do |format|
format.html { redirect_to tasks_url, notice: 'Task was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_task
#task = Task.find(params[:id])
end
def set_project_session
#project_session = ProjectSession.find(params[:project_session_id])
end
def task_params
params.require(:task).permit(:name, :description)
end
end
routes:
Rails.application.routes.draw do
get 'hr_dashboard/index'
resources :roles
resources :project_sessions
devise_for :users
resources :tasks
resources :projects do
resources :project_sessions, except: [:show, :index]
end
resources :project_sessions do
resources :tasks, except: [:show, :index]
end
authenticated :user do
root 'admindashboard#index', as:"authenticated_root"
end
root 'welcome#index'
get 'userdashboard/index'
get 'admindashboard/index'
get 'welcome/index'
end
View for creating new task
<div class="container">
<h1>New Task</h1>
<%= form_for(#task) do |f| %>
<% if #task.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#task.errors.count, "error") %> prohibited this task from being saved:</h2>
<ul>
<% #task.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<%= link_to 'Back', tasks_path %>
</div>
I figured it out!
I had forgotten to add #project_session in
<%= form_for([#project_session, #task]) do |f| %>
just added that and it worked.
In your create action of Task controller, you have to add:
#task= #project_session.tasks.build(task_params)
Because right now, you're not telling the task, to build from the project_session (or with respect to the project_session) you're just telling it to create a new task.
#task = Task.new
And in the routes.rb file, you've prepared for that to happen by nesting the resources, so it's currently looking for the ID of a task that belongs_to project_session. But can't find any.
And also, in your form_for element when creating a ProjectSssion you have let that form know which route it should belong to - I guess you could say - since you still have the un-nested resources available:
resources :tasks
If project_sessions shouldn't be creatable without a parent, there's no reasons for keeping that, so you should just remove it.
Anyways, here's what the form_for should look like:
<%= form_for([#project_session, #task]) do |f| %>

undefined method `each' for nil:NilClass, how to set my instance variable?

I am trying to search by keywords some titles, I need help to set my instances variables tutos.... ( It seems that I need two instances #tutos... but I don't know how to do?
I tried:
#tutos = Tuto.all.includes(:user, :category) || Tuto.search(params[:search]) but I have the same error...
I have undefined method each' for nil:NilClass even though I have the instance variable set... thanks for your help
my controller
class TutosController < ApplicationController
before_action :authenticate_user!, only: [:new, :create]
before_action :set_tuto, only: [:show, :edit, :update, :destroy, :upvote]
def index
#tutos = Tuto.all.includes(:user, :category)
keyword_search
#categories = Category.all
end
def keyword_search
#tutos = Tuto.search(params[:search])
end
def show
#tuto = Tuto.find(params[:id])
#user = User.all
end
def new
#tuto = Tuto.new
end
def edit
end
def create
#tuto = Tuto.new(tuto_params)
#tuto.user_id = current_user.id
respond_to do |format|
if #tuto.save
flash[:success] = "Test"
format.html { redirect_to #tuto, notice: 'Tuto was successfully created.' }
format.json { render :show, status: :created, location: #tuto }
else
format.html { render :new }
format.json { render json: #tuto.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #tuto.update(tuto_params)
format.html { redirect_to #tuto, notice: 'Tuto was successfully updated.' }
format.json { render :show, status: :ok, location: #tuto }
else
format.html { render :edit }
format.json { render json: #tuto.errors, status: :unprocessable_entity }
end
end
end
def destroy
#tuto.destroy
respond_to do |format|
format.html { redirect_to tutos_url, notice: 'Tuto was successfully destroyed.' }
format.json { head :no_content }
end
end
def upvote
#tuto.upvote_by current_user
redirect_to :back
end
private
def set_tuto
#tuto = Tuto.find(params[:id])
end
def tuto_params
params.require(:tuto).permit(:title, :content, :id, :user_id, :category_id)
end
end
my index
.container
.row
.col-xs-12
h1.text-gray Tutorials
h4 Search by Title
=form_tag tutos_path, :method => 'get' do
=text_field_tag :search, params[:search]
=submit_tag "Search", class:'btn btn-default'
= form_tag '/tutos', method: 'get' do
select[name="category.id"]
- #categories.each do |category|
| <option value="
= category.id
| ">
= category.name
input[type="submit" value="Search", class="btn btn-default"]
.col-xs-12
-if user_signed_in?
= link_to "Create a tuto", new_tuto_path, class:"btn btn-success"
br
br
#tutos.transitions-enabled
-#tutos.each do |tuto|
.box.panel-default
= link_to image_tag(image_by_category(tuto.category.try(:name))), tuto_path(tuto)
h3 = link_to tuto.title, tuto_path(tuto), class:"title-link"
h6
| Created by:
span<>
= tuto.user.full_name
br
span.glyphicon.glyphicon-heart
span<>
= tuto.get_upvotes.size
br
br
tuto model
class Tuto < ActiveRecord::Base
acts_as_votable
belongs_to :user
belongs_to :category
validates :category_id, presence: true
def self.search(search)
if search
where(["title LIKE ?","%#{search}%"])
else
all
end
end
end
Update your index like this. you don't need keyword_search.
def index
if params[:search]
#tutos = Tuto.search(params[:search]).includes(:user, :category)
else
#tutos = Tuto.all.includes(:user, :category)
end
#categories = Category.all
end
Hope this helps.
def index
#tutos = Tuto.all.includes(:user, :category)
keyword_search // **Here you are overriding #tutos... First have no meaning always**
#categories = Category.all
end
def keyword_search
#tutos = Tuto.search(params[:search])
end
2 ->
def self.search(search)
if search
where(["title LIKE ?","%#{search}%"])
else
all
end
end
It will give nil if searched data is not available, so you should handle this.. otherwise each with nil occurs

Rails How to pass params in cross model system

I am unable to figure out the method to pass params to a link tag. What I want to do is When someone clicks on the Join Group link, the Membership model shall have a new row with group_id as the current group id and the user id as the current user id. The Membership model currently consists of two columns : user_id and group_id that maps users to groups. Can anyone help me with the mistake I am making.
Here is the code
Groups : show.html.erb
<p id="notice"><%= notice %></p>
<p>
<strong>Title:</strong>
<%= #group.title %>
</p>
<p>
<strong>Desc:</strong>
<%= #group.desc %>
</p>
<p>
<strong>Creator:</strong>
<%= #creator.first_name %>
</p>
<%= link_to 'Join Group', memberships_path(:group_id => #group.id, :user_id => current_user.id ), method: :post %>
<%= link_to 'Edit', edit_group_path(#group) %> |
<%= link_to 'Back', groups_path %>
and here is the Memberships controller
class MembershipsController < ApplicationController
before_action :set_membership, only: [:show, :edit, :update, :destroy]
# GET /memberships
# GET /memberships.json
def index
#memberships = Membership.all
end
# GET /memberships/1
# GET /memberships/1.json
def show
#membership = Membership.find(params[:id])
#user = User.find(#membership.user_id)
#group = Group.find(#membership.group_id)
end
# GET /memberships/new
def new
#membership = Membership.new
end
# GET /memberships/1/edit
def edit
end
# POST /memberships
# POST /memberships.json
def create
#membership = Membership.new(membership_params)
respond_to do |format|
if #membership.save
format.html { redirect_to #membership, notice: 'Membership was successfully created.' }
format.json { render :show, status: :created, location: #membership }
else
format.html { render :new }
format.json { render json: #membership.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /memberships/1
# PATCH/PUT /memberships/1.json
def update
respond_to do |format|
if #membership.update(membership_params)
format.html { redirect_to #membership, notice: 'Membership was successfully updated.' }
format.json { render :show, status: :ok, location: #membership }
else
format.html { render :edit }
format.json { render json: #membership.errors, status: :unprocessable_entity }
end
end
end
# DELETE /memberships/1
# DELETE /memberships/1.json
def destroy
#membership.destroy
respond_to do |format|
format.html { redirect_to memberships_url, notice: 'Membership was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_membership
#membership = Membership.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def membership_params
params.require(:membership).permit(:user_id, :group_id)
end
end
You should have a method in your controller that handles adding a user to a group, something like:
class MembershipsController < ApplicationController
...
def join_group
#user = User.find(params[:user]
#user.group_id = params[:group_id].to_i
end
...
end
Then routes.rb needs a path to that controller method:
get 'memberships/join_group' => 'memberships#join_group'
which will result in a path like memberships_join_group_path that you will use in your link_to:
<%= link_to 'Join Group', memberships_join_group_path(:group_id => #group.id, :user_id => current_user.id ) %>
The path you're currently using is sending params to your index method.
try
def create
#member = current_user.members.build(:group_id => params[:group_id])
if #member.save
flash[:notice] = "You have joined this group."
redirect_to members_path
else
flash[:error] = "Unable to join."
redirect_to members_path
end
end
and use memberships instead of members

Resources