Form_for helper - ruby-on-rails

I have 2 models that are associated with each other via a join table:
class Book < ActiveRecord::Base
has_many :reviews
end
class Reader < ActiveRecord::Base
has_many :reviews
end
class Reviews < ActiveRecord::Base
belongs_to :reader
belongs_to :book
end
Right now, I am able to update a review (which I created manually in the console) on route:
readers/:id/books
The above route was create using rails' member method:
resources :readers
member do
get 'books'
end
end
The update action in reviews controller (reviews#update) is defined like so:
def update
#reader = current_reader
#review = Review.find_by_reader_id(#reader.id)
#book = Book.find(params[:review][:book]
if #reader.books.include?(#book)
#review.update_attributes(review_params)
redirect_to (#reader)
else
flash[:error] = 'You can only edit reviews that belong to you'
end
end
My form_for reviews (reviews#update) looks like this:
Reader Reviews:
<% book.reviews.each do |review| %>
<% if current_reader == (review.reader) %>
<%= review.content %> written by <%= review.reader.name %>
<% if current_reader.reviews.include?(review) %>
<%= form_for ([book, review]) do |f| %>
<div class="field">
<%= f.hidden_field :book, :value => book.id %>
<%= f.text_area :content, placeholder: "compose new review" %>
</div>
<%= f.submit "Update", class: "btn btn-large btn-primary" %>
<% else %>
<%= form_for ([book, review]) do |f| %>
<div class="field">
<%= f.hidden_field :book, :value => book.id %>
<%= f.text_area :content, placeholder: "compose new review" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
<% end %>
<% end %>
<% end %>
The above works for update. But the 2nd form doesn't.
My intent is to check for a review => if there is one - display a form so that reader can update review; if there isn't one then display a form so that reader can create a review. I have a private method in reviews controller that checks to make sure that a reader has a book before either action is carried out (a before_action method I guess).
The first form works well (the update form) but the second does not - the form is not displayed at all. I have tried various things to get the form to display but no luck. Can you please me determine the best way to resolve this issue?
Thank you very much!

There could be several issues:
elsif
The likely reason your other form won't show will be that your elsif logic won't be correct
I had a look at the .include? Ruby function, and it seems to just be for arrays. Why not try using .exists? instead?
<% elseif !current_reader.reviews.exists?(review) %>
You may have to use review.id or similar to get the correct response. Failing that, why don't you just use <% else %>?
form_for
The second issue may be with your second form_for
<%= form_for [:book, review] do |f| %>
You're currently passing a local variable called book to the form_for builder. Although this is probably correct (I can't find your reference to creating book), I've found it best to put a symbol in the nested form (to show Rails which data it needs to use)

Could you try using else instead of elsif !current_reader.reviews.include?(review)?
Also, it's elsif and not elseif. The problem should not because of this - The page would not have loaded in the first place if this is the case.

UPDATE
I fixed my first error by updating my forms:
Reader Reviews:
<% book.reviews.where(reader_id: current_reader.id).each do |review| %>
<li>
<span><%= review.content %> writen by <%= review.reader.name %> </span
<%= form_for ([book, review]) do |f| %>
<div class="field">
<%= f.hidden_field :book, :value => book.id %>
<%= f.text_area :content, placeholder: "compose new review" %>
</div>
<%= f.submit "Update", class: "btn btn-large btn-primary" %>
<% end %>
</li>
<% if book.reviews.where(reader_id: current_reader.id).size == 0 %>
<%= form_for ([book, book.reviews.build]) do |f| %>
<div class="field">
<%= f.hidden_field :book, :value => book.id %>
<%= f.text_area :content, placeholder: "compose new review" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
<% end %>
<% end %>
This displays the forms (both post and update). BUT I got this error when I tried to post a new review:
(rdb:35) #review
#<Review id: nil, reader_id: 101, content: "Trial", created_at: nil, updated_at: nil, book_id: nil>
(rdb:35) review_params
Unpermitted parameters: book
{"content"=>"Trial"}
(rdb:35)
So I changed my create action for review to make sure book_id isn't nill:
def create
#reader = current_reader
# #book = Book.find(params[:book_id])
#book = Book.find(params[:review][:book])
if #reader.reviews.where(book_id: #book.id).exists?
flash[:error] = "You already reviewed this book"
else
#review = current_reader.reviews.create(:book_id => params[:book_id.to_i, :content => review_params[:content])
debugger
if #review.save
flash[:success] = "Review created"
redirect_to reader_path(#reader)
else
flash[:error] = "You can only review books that are in your library"
redirect_to reader_path(#reader)
end
end
end
Also changed how I defined review_params:
def review_params
params.require(:review).permit(:content, :book_id)
end
All this changes gave the desired results. My code isn't dry AT ALL but the most important thing to me at this point is getting things to work. Here is to hoping I don't break it again. Thanks for your help #RichPeck

Related

Submit values with link_to in Rails 5.1

So currently i have a link_to, where signed in users can click on:
<%= link_to "Enroll", [#task.project, #task] %>
The user has an association with the project, through subscription. To create a new subscription for a user with a project, i wrote some simple form for it.
<%= form_for([#project, #subzz]) do |f| %>
<%= f.hidden_field :project_id, :value => #project.id %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Which works fine and creates the association. However, i want that the user is able to create the subscription whenever he clicks on 'enroll' instead of a second, extra submit button.
Any ideas how to approach this? I thought about using jQuery, but not sure how to inject the ids with it and if its the 'right' way to do it.
Thanks in advance everyone!
EDIT:
When using the method posted as answer, i get:
param is missing or the value is empty: sub
My updatet form:
<%= form_for([#project, #subzz], html: {role: "form", id: "project_form"}) do |f| %>
<%= hidden_field_tag :project_id, :value => #project.id %>
<%= hidden_field_tag :user_id, :value => current_user.id %>
<%= link_to "Enroll", [#task.project, #task], :onclick => "$('#project_form').submit() "%>
<% end %>
subs_controller.rb
class SubsController < ApplicationController
def create
#subz = Sub.create(sub_params)
project = #subz.project
redirect_to root_path
end
private
def sub_params
params.require(:sub).permit(:project_id, :user_id)
end
end
You can be using the existing form and link_to, just edit some like edit the dorm_tag like this
<%= form_for([#project, #subzz], html: {role: "form", id: "project_form"}) do |f| %>
and remove the button into form like this one
<div class="actions">
<%= f.submit %>
</div>
and edit the link_to like this
<%= link_to 'Enroll', "", :onclick => "$('#project_form').submit()" %>
it will work
Update
You can achieve this without a form, comment out this form and edit the link like below
<%= link_to 'Enroll', subs_path(project_id: #project.id, user_id: current_user.id), method: :post %>
and the create method update like below
def create
#subz = Sub.new(sub_params)
if #subz.save
flash[:success] = 'Sub was successfully submited.'
redirect_to root_path
else
flash[:danger] = 'Sub not submited'
redirect_to request.referer
end
end
that is easier
Or if you keep before one with form then the link out from the form and the create method edit like the following
def create
#subz = Sub.new(sub_params)
if #subz.save
flash[:success] = 'Sub was successfully submited.'
redirect_to root_path
else
flash[:danger] = 'Sub not submited'
redirect_to request.referer
end
end
and the form will look like this
<%= form_for([#project, #subzz], html: {role: "form", id: "project_form"}) do |f| %>
<%= hidden_field_tag :project_id, :value => #project.id %>
<%= hidden_field_tag :user_id, :value => current_user.id %>
<% end %>
<%= link_to "Enroll", [#task.project, #task], :onclick => "$('#project_form').submit() "%>
if you confused this [#task.project, #task] on link tag then use direct link
So i came up with the following solution:
I've added the sub handling to the application_controller, so that its availiable for the project_controller. I also added the project, tasks as a reference, so that i am able to redirect to a task via the sub_controller, instead of the project_controller.
application_controller.rb
def create
#subs = Sub.new(sub_params)
project = #subs.project
taskz = project.tasks.first
if #subs.save
redirect_to [taskz.project, taskz]
end
end
private
def sub_params
params.require(:sub).permit(:project_id, :user_id)
end
Inside the show.html.erb from the project_controller, i use the old form:
<%= form_for([#project, #subz] do |f| %>
<%= f.hidden_field :project_id, :value => #project.id %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.submit "Submitted" %>
<% end %>
which works fine. Thanks for any previous help!

How do i make one form for two related models?

I want to write data for 2 different models that are related to each other like this:
class Post < ActiveRecord::Base
has_many :roads
accepts_nested_attributes_for :roads, :allow_destroy => true
end
class Road < ActiveRecord::Base
belongs_to :post
end
Of course de roads table has a "post_id column". My form_for in the post view looks like this
<%= form_for #post do |f| %>
<div class="form-group">
<%= f.label :Tu_Nombre %>
<%= f.text_field :creador, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :año %> (Cuando fue tu experiencia?)
<%= f.text_field :año, class: "form-control" %>
</div>
<div class="form-group">
<%= fields_for :roads do |r| %>
<%= r.label :principio %> (desde?)
<%= r.text_field :principio, class: "form-control" %>
<%= r.label :final %> (hasta?)
<%= r.text_field :final, class: "form-control" %>
<% end %>
</div>
<div class="form-group">
<%= f.label :historia %> (Cuentanos la historia de tu viaje!)
<%= f.text_area :historia, class: "form-control", size: "50x15" %>
</div>
<div>
<%= f.submit class: "btn btn-success" %>
</div>
<% end %>
Finally my posts_controller create method
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post, notice: 'Post was successfully created.'
else
render :new
end
end
with the private
def post_params
params.require(:post).permit(:historia, roads_attributes: [:pricipio, :final, :post_id])
end
The post is submitted and if i check the console all the atributes for post has been saved but the ones for road, has not. How can i use just one form two make records for these two models and that the post_id gets registered so i can relate the tables?
Thanks a lot!!
Update your posts_controller new method to include .build
def new
#post = Post.new
#post.roads.build
end
Please read the Nested Forms in Form Helpers chapter of Rails Guide. You could use fields_for to build the nested attributes form for your roads and permit nested attributes like the example. Below is an example from rails guide, you could try and figure it out yourself.
_form.html.erb
<%= form_for #person do |f| %>
Addresses:
<ul>
<%= f.fields_for :addresses do |addresses_form| %>
<li>
<%= addresses_form.label :kind %>
<%= addresses_form.text_field :kind %>
<%= addresses_form.label :street %>
<%= addresses_form.text_field :street %>
...
</li>
<% end %>
</ul>
<% end %>
permit nested attributes in people_controller.rb
def person_params
params.require(:person).permit(:name, addresses_attributes: [:id, :kind, :street])
end
Attach your fields_for to the parent form builder like so:
<%= f.fields_for :roads do |r| %>

How to pass an :id to a link_to through a has_many :through relationship to delete a join table column. (Rails)

This is my first post on StackOverflow, and I'm relatively new to coding in general, so I apologize in advance if I accidentally make some breach of etiquette.
I'm trying to delete a collaborator to a wiki that has been added by the owner, but now he wants to remove their permissions.
This is done from within my wiki#edit view:
<div class="row">
<%= render partial: 'wikis/form' %>
<br>
<%= render partial: 'collaborators/collaborator'%>
<h3> Collaborators: </h3>
<% #wiki.users.each do |c| %>
<li><%= c.username %></li>
<% if #wiki.user == current_user && current_user.premium? %>
<%= link_to "", wiki_collaborator_path(#wiki, #collaborators), method: :delete, remote: true, class: "glyphicon glyphicon-remove" %>
<% end %>
<% end %>
The wikis/_form partial is pretty standard:
<div class="col-md-8">
<div class="form-group">
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control', placeholder: "Enter wiki title" %>
</div>
<div class="form-group">
<%= f.label :body %>
<%= f.text_area :body, rows: 8, class: 'form-control', placeholder: "Enter wiki body" %>
</div>
<% if current_user.admin? || current_user.premium? %>
<div class="form-group">
<%= f.label :private, class: 'checkbox' do %>
<%= f.check_box :private %> Private wiki
<% end %>
</div>
<% end %>
<div class="form-group">
<%= f.submit "Save", class: 'btn btn-primary' %>
</div>
<% end %>
</div>
and the collaborators/_collaborator partial creates the initial collaborator:
<% if #wiki.user == current_user && current_user.premium? %>
<%= form_for [#wiki, Collaborator.new] do |f| %>
<%= email_field_tag :collaborator %>
<%= f.submit "Add collaborator", class: 'btn btn-primary' %>
<% end %>
<% end %>
This is the Collaborator controller:
before_action :set_wiki
def create
#collaborator = User.find_by_email(params[:collaborator]) #pulled from email tag in collaborator form_for partial
Collaborator.create(user_id: #collaborator.id, wiki_id: #wiki.id)
if #collaborator.save
flash[:notice] = "#{#collaborator.username} at #{#collaborator.email} has been added as a collaborator to #{#wiki.title}"
else
flash[:error] = "Adding of collaborator failed"
end
redirect_to #wiki
end
def delete
#collaborator = #wiki.users.find(params[:id])
if #collaborator.destroy
flash[:notice] = "#{#collaborator.username} at #{#collaborator.email} has been removed from wiki: #{#wiki.title}"
else
flash[:error] = "Removal of collaborator failed"
end
redirect_to #wiki
end
private
def set_wiki
#wiki = Wiki.find(params[:wiki_id])
end
end
The Collaborator Model just belongs_to both User and Wiki, and The Wiki model looks like this:
class Wiki < ActiveRecord::Base
belongs_to :user
validates :user, presence: true
#Final refactoring eliminates need for delegate method
has_many :collaborators, dependent: :destroy
has_many :users, through: :collaborators
scope :visible_to, -> (user) { user ? all : where(private: false) }
validates :title, length: { minimum: 5 }, presence: true
validates :body, length: { minimum: 20 }, presence: true
end
The error I'm getting back from my local server is
No route matches {:action=>"destroy", :controller=>"collaborators", :id=>nil, :wiki_id=>"104"} missing required keys: [:id]
I've looked everywhere to solve this problem, but I inherently have some trouble understanding the nested routing for has_many :through associations and when I look for answers it seems like everyone has written their method calls differently than mine. Everything in my app functions except this link_to method, so I'd rather not rewrite it just to match someone else's code unless I really messed up somewhere.
I've tried putting #collaborator.id as well as a few other things just to try to brute force a solution, but nothing is working and I have no idea what I'm looking for here.
Any help at all would be greatly appreciated. Thank you ^^
No route matches {:action=>"destroy", :controller=>"collaborators", :id=>nil, :wiki_id=>"104"} missing required keys: [:id]
The base problem is here:
<%= link_to "", wiki_collaborator_path(#wiki, #collaborators), method: :delete, remote: true, class: "glyphicon glyphicon-remove" %>
The error states that your #collaborators var does not pass the singular id to the link_to method. This is because I presume #collaborators to be a collection.
--
You need to pass a single collaborator object:
<%= link_to "", wiki_collaborator_path(#wiki, c) ...
Since you're using a .each loop with #wiki.users, each user (which I presume to be a collaborator) has the local variable of c assigned:
<% #wiki.users.each do |c| %>
Since you're new, I'd strongly recommend using font-awesome-rails over glyphicon. The main benefit is the fa_icon helper, which would allow you to use:
<%= link_to fa_icon("close"), wiki_collaborator_path(#wiki, c), method: :delete, remote: true %>
Try to iterate through the collaborators of the #wiki to get the correct link. The collaborators of a wiki are in your case the wiki's users, because they are connected via the collaborators relation. So you just do:
<% #wiki.users.each do |collaborator| %>
<%= link_to "", wiki_collaborator_path(#wiki, collaborator), method: :delete, remote: true, class: "glyphicon glyphicon-remove" %>
<% end %>

rails 4 how to pass post_id to the db

i have some problems with my app, i have posts, posts has_many responces
when i create new responce to the post, not added in the db 'responce' post_id
my routes.rb
resources :categories do
resources :posts
end
resources :posts do
resources :responces
end
controller
class ResponcesController < ApplicationController
def new
#post = Post.find(params[:post_id])
#responce = #post.responces.new(post_id:params[:post_id])
end
def create
#responce = current_user.responces.build(responce_params)
#post = Post.find(params[:post_id])
if #responce.save
flash[:success] = "Вы откликнулись на задание"
redirect_to post_path #post
else
render 'new'
end
end
def show
end
private
def responce_params
params.require(:responce).permit(:price, :comment, :post_id)
end
end
view
<%= form_for([#post, #responce]) do |f| %>
<%= f.text_area :price %>
<%= f.submit "GO", class: "btn btn-large btn-primary" %>
<% end %>
but if add to the view this
<%= f.collection_select :post_id, Post.all, :id, :name %>
rails create post_id to the db
help
You are doing several things the wrong way.
First: I don't think you need two separate resources for the same model. I'd recomend nesting all three resources upon each other like this.
resource :categories do
resource :posts do
resource :responces
end
end
This way you'll be able to find the needed category_id and post_id in the params hash.
I'd also recomend adding :shalow => true to the :categories resource to make your routes a bit prettier.
Second: you need to assign the params[:post_id] in your create action, like this.
#responce = current_user.responces.build(responce_params)
#responce.post_id = params[:post_id]
#post = #responce.post
Alternatevely you can just add a hidden field to your form like I show below, but it I don't like that approach, 'cause it can lead to security risks.
<%= form_for([#post, #responce]) do |f| %>
<%= f.text_area :price %>
<%= f.hidden_field :post_id, :value => #post.id %>
<%= f.submit "GO", class: "btn btn-large btn-primary" %>
<% end %>
In your form you aren't passing in the post_id. You probably want something like this:
<%= form_for([#post, #responce]) do |f| %>
<%= f.text_area :price %>
<%= f.hidden_field :post_id, :value => #post.id %>
<%= f.submit "GO", class: "btn btn-large btn-primary" %>
<% end %>
The hidden field will pass the id of the current post into your form as the post_id parameter.

rails form data not getting saved to db

I'm new to rails and trying to make a simple site to start learning. When I submit my form, however, the data isn't saved to the db. I'm really not sure what's wrong, I've been trying to figure it out for a while. If I make a record in the rails console and save it, that one successfully shows up in the db (and on the index page).
calculate.rb:
class Calculate < ActiveRecord::Base
attr_accessible :number, :root
end
calculates_controller.rb:
class CalculatesController < ApplicationController
def index
#calculate = Calculate.all
end
def new
#calculate = Calculate.new
end
def create
#calculate = Calculate.new(params[:calculate])
if #calculate.save
redirect_to '/calculates'
else
render 'new'
flash[:notice] = "Didn't work"
end
end
end
new.html.erb:
<%= form_for(#calculate) do %>
<%= label_tag(:number, "Enter the number") %>
<%= text_field_tag :number %>
<%= label_tag(:root, "root") %>
<%= text_field_tag :root %>
<%= submit_tag("Submit") %>
<% end %>
if you are using form_for, use the form_for syntax
<%= form_for(#calculate) do |form| %>
<%= form.label :number %>
<%= form.text_field :number %>
<%= form.label :root %>
<%= form.text_field :root %>
<%= form.submit "Submit" %>
<% end %>
this will automatically handle the routes if the #calculate is new object it will submit it to create or if it is already saved it will send a put request to edit action
Ah hah! I updated my view to:
<%= form_for #calculate, :url => { :action => "create" } do |f| %>
<%= f.label :number %>
<%= f.text_field :number %>
<%= f.label :root %>
<%= f.text_field :root %>
<%= submit_tag("Submit") %>
<% end %>
And now it works. Awesome.

Resources