rails link_to posts to incorrect id - why? - ruby-on-rails

I want my User to be able to change a boolean on a Share that they own, but my attempt at implementation updates the wrong record.
When I go to the show page for an Item with id:7, my controller loads the associated Share objects by looking for Shares that have item_id set to 7. When I then click the Hide or Show buttons, my code updates the associated Share's active attribute, and then redirects to that same Item.
But if I go to the show page for an Item with id:3, and click those same buttons, my code redirects to and updates the active attribute for the Share with item_id:7, instead of item_id:3. Can anyone give me an idea as to why this is happening?
My Share model:
class Share < ActiveRecord::Base
belongs_to :user
belongs_to :item
def activate
self.active = true
save
end
def deactivate
self.active = false
save
end
end
My Item model:
class Item < ActiveRecord::Base
has_many :shares
end
In my ItemsController#show action, I have this:
def show
#item = Item.friendly.find(params[:id])
#owned_share = current_user.shares.find_by(item_id: #item.id)
end
In my SharesController, I have this:
def activate
#owned_share = current_user.shares.find_by(params[:item_id])
#owned_share.activate
respond_to do |format|
format.html { redirect_to item_path(#owned_share.item) }
format.json { render :index, status: :ok, location: #owned_share }
end
end
def deactivate
#owned_share = current_user.shares.find_by(params[:item_id])
#owned_share.deactivate
respond_to do |format|
format.html { redirect_to item_path(#owned_share.item) }
format.json { render :index, status: :ok, location: #owned_share }
end
end
And in my Item show view, I have this:
<% if #owned_share.active == true %>
<div class="eight wide column">
<%= link_to "Hide", share_deactivate_path(#owned_share.item), class: "button wide-button functional-red-button", method: :post %>
</div>
<% else %>
<div class="eight wide column">
<%= link_to "Show", share_activate_path(#owned_share.item), class: "button wide-button functional-mint-button", method: :post %>
</div>
<% end %>

As stated in the comments, the param you're receiving isn't item_id, but share_id, that's why despite you modify your query adding the attribute which to look for, it doesn't give you the expected result.
Update the param which to use for getting user's share, like:
#owned_share = current_user.shares.find_by(item_id: params[:share_id])
Although in this case isn't clear why you're using share_id to look for an item_id, most probably you could update that part too.
As both actions share some specific functionality, you could make just one that just updates the active attribute "flipping" its value:
# model
def toggle_active
update(active: !active)
end
# controller
def update_active_status
#owned_share = current_user.shares.find_by(item_id: params[:share_id])
#owned_share.toggle_active
respond_to do |format|
format.html { redirect_to item_path(#owned_share.item) }
format.json { render :index, status: :ok, location: #owned_share }
end
end
It gets the current user's shares active value and alternate it by using !. Notice that if they don't have a default value, a negation of nil returns true.
!true # false
!false # true
!nil # true
Note #owned_share.active == true can also be #owned_share.active? or #owned_share.active.

Because this:
#owned_share = current_user.shares.find_by(params[:item_id])
should be:
#owned_share = current_user.shares.find_by_item_id(params[:item_id])

Related

Rails 4 is not saving into the database with this method?

Trying to save parameters from url into a database.
I have a link:
- #kits.each do |kit|
= link_to 'Submit Video', new_challenge_path(kit: kit)
#this will append a new parameter into the url
The link goes to a form page with this:
= simple_form_for #challenge, html: { class: "form-horizontal" } do |f|
= f.input :video_title
= f.input :video_url
= f.input :video_desc, as: :text
= f.button :submit, "Upload video"
In my controller, I have this:
def create
#challenge = Challenge.new(challenge_params)
#challenge.kit_id = params[:kit]
respond_to do |format|
if #challenge.save
format.html { redirect_to #challenge, notice: 'Challenge was successfully created.' }
format.json { render :show, status: :created, location: #challenge }
else
format.html { render :new }
format.json { render json: #challenge.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_challenge
#challenge = Challenge.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def challenge_params
params.require(:challenge).permit(:video_title, :video_url, :video_desc, :kit_id)
end
Association between kit and challenge
class Challenge < ActiveRecord::Base
belongs_to :kit, counter_cache: true
end
class Kit < ActiveRecord::Base
has_many :challenges
end
The parameter doesn't save into the :kit_id. Doesn't this: #challenge.kit_id = params[:kit] supposed to take care of saving it?
You are right to do the kit_id assignment in controller, not in the form, since doing so in the form (even by means of using the hidden field) is not secure, because changing the value of a hidden_field is just a matter of inspecting the elements of the page.
What you are doing wrong, is I assume that params[:kit] is just nil.
You would probably want to use params[:kit_id].
If that won't help, put binding.pry here:
# ... code omitted
#challenge.kit_id = params[:kit]
binding.pry
# ... code omitted
and check the value of params in opened console.

"param is missing or the value is empty: color" error in controller

I followed a tutorial on YouTube involving making a simple model, printing out the results and updating the model with a form and did a find and replace for what I was trying to accomplish ("text files, the tutorial involved images)
Everything worked up until around the time I just wanted an single index page and tried merging all the controller logic into the index.
I'm currently getting an error reading param is missing or the value is empty: color on params.require in the controller below.
class ColorsController < ApplicationController
before_action :find_color, only: [:destroy]
def index
#colors = Color.all.order("created_at DESC")
#color = Color.new(color_params)
end
def destroy
#color.destroy
end
private
def find_color
#color = Color.find(params[:id])
end
def color_params
params.require(:color).permit(:file)
end
end
What I take from this is that it's not recognizing the #color instance variable, but I don't know or why I'm supposed to rectify this.
Model:
class Color < ActiveRecord::Base
has_attached_file :file
validates_attachment_content_type :file, :content_type => ["application/xml"]
end
Form:
= simple_form_for #color do |f|
= f.input :file
= f.submit
Explanation of what I'm doing wrong is much appreciated.
param is missing or the value is empty: color
You should change your index method to below
def index
#colors = Color.all.order("created_at DESC")
#color = Color.new #notice the change here
end
Also, you should define a create method like below
def create
#color = Color.new(color_params)
respond_to do |format|
if #color.save
format.html { redirect_to #color, notice: 'Color was successfully created.' }
format.json { render :show, status: :created, location: #color }
else
format.html { render :new }
format.json { render json: #color.errors, status: :unprocessable_entity }
end
end
end

Rails HABM articles and categories, can't get categories links to view associated articles

Im making a news website and created Articles and Categories models that have belongs_to_and_have_many association.
class Category < ActiveRecord::Base
has_and_belongs_to_many :articles
validates :name, presence: true, length: { in: 2..20 }, uniqueness: true
def to_s
"#{name}"
end
end
class Article < ActiveRecord::Base
has_many :comments
has_and_belongs_to_many :categories
end
I created joined table
create_table "articles_categories", id: false, force: true do |t|
t.integer "article_id"
t.integer "category_id"
end
Now, I managed to display articles and belonging categories on index and show sites. I want to make category links leading to sites with articles associated with a single category (e.x. sport => all articles with that category). In categories-index.html.erb:
<h1>Categories</h1>
<div class="row">
<% #categories.each do |category| %>
<div class='col-sm-3'>
<h2><%= link_to category %></h2>
<h3>Articles</h3>
<% category.articles.each do |article| %>
<%= link_to article.title, article %>
<% end %>
</div>
<% end %>
</div>
The links appear on the site but they don't route to anything. How can I make those links route to appropriate site?
class CategoriesController < ApplicationController
def index
#categories = Category.all
end
def show
end
def new
#category = Category.new
#articles = Article.all
end
def edit
#articles = Article.all
end
def create
#category = Category.new(category_params)
respond_to do |format|
if #category.save
format.html { redirect_to #article, notice: 'Category added' }
format.json { render :show, status: :created, location: #article }
else
format.html { redirect_to #article, notice: 'Category not added'}
format.json { render json: #category.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #category.update(category_params)
format.html { redirect_to #category, notice: 'Category was successfully updated.' }
format.json { render :show, status: :ok, location: #category }
else
format.html { render :edit }
format.json { render json: #category.errors, status: :unprocessable_entity }
end
end
end
def destroy
#category.destroy
redirect_to article_path(#article)
end
private
def category_params
params.require(:category).permit(:name, :article_ids => [])
end
end
The link_to helper requires at least two parameters, the content to display and the place to route to.
Your category link does not include the latter
<%= link_to category %>
Depending on where you want the link to go, try something like this
<%= link_to category.name, category %>
This will link to the CategoriesController#show action. In that action, if you want to show articles for the given category, you can include something like this in the controller
# CategoriesController
def show
#category = Category.find(params[:id])
#articles = #category.articles
end

Rails 4 ActiveRecord Model: correctly using _was to separate two quantites

I'm building an ecommerce app and I'm a bit stuck. I don't want to create a new line_item if the product is the same. Here is my cart.rb model
class Cart < ActiveRecord::Base
has_many :line_items, dependent: :destroy
has_many :products
def add_product(product_id, size, color, quantity)
current_item = line_items.find_by_product_id(product_id)
if current_item
current_item.quantity = 20
current_item.quantity += current_item.quantity_was
else
current_item = line_items.build(product_id: product_id, size: size, color: color, quantity: quantity)
end
current_item
end
def total_price
line_items.to_a.sum { |item| item.total_price }
end
end
line_items controller (where a new line gets created in the cart):
class LineItemsController < InheritedResources::Base
def create
#cart = current_cart
product = Product.find(params[:product_id])
size = params[:product][:size]
color = params[:product][:color]
quantity = params[:product][:quantity]
#line_item = #cart.add_product(product.id, size, color, quantity)
respond_to do |format|
if #line_item.save
format.html { redirect_to products_customer_cart_url,
notice: 'You have added an item to your cart.' }
#format.js { #current_item = #line_item }
format.json { render json: #line_item,
status: :created, location: #line_item }
else
format.html { render action: "new" }
format.json { render json: #line_item.errors,
status: :unprocessable_entity }
end
end
end
def destroy
#cart = current_cart
#line_item = LineItem.find(params[:id])
#line_item.destroy
respond_to do |format|
format.js {}
end
end
end
products show.html.erb below (where you select the item):
<%= form_for(#product, url: line_items_path(product_id: #product.id), html: {method: "update"}) do |f| %>
<%= image_tag("https://s3.amazonaws.com/blah/mens_black_front.jpg" %>
<table>
<tr><td>Size:</td> <%= f.select(:size, #male_shirt_sizes.map { |value| value }, :include_blank => 'Select Size') %></td><tr>
<tr><td>Color:</td> <%= f.select(:color, #shirt_colors.map { |value| value }, :include_blank => 'Select Color') %></td><tr>
<tr><td>Quantity:</td> <%= f.select(:quantity, #quantity.map { |value| value }, :include_blank => 'Select Quantity') %></td><tr>
</table>
<%= f.submit "Add to Cart", :class => "btn btn-mega btn-primary", :id => "add-to-cart-btn" %>
<% end %>
-Now, as you can see I just put the number 20 in there for now to test it out.
-So say I go to add an item initially, quantity of 4.
-That line_item will now have a quantity of 4.
-Now when I go to add that same product with a different quantity (whether it be 1, 2, 3, 4 or 5), it'll add the int 20 (as it should, because 20 is hardcoded in there).
But how would I dynamically add the 'new' quantity, to add the initial quantity to the latest quantity?
I have answered a similar question here. Will post a summary
But how would I dynamically add the 'new' quantity, to add the initial quantity to the latest quantity?
The problem is your submit button takes it to the create action of your item and hence creating a new item inside your cart
Fix:
You need to make a new action with post route request(to find that item) and then update its quantity inside that action.
If you look at your button or rather i should say form for creating a new item then it's the path or url part in your form which takes it to your method inside your controller. If you change its url then it will take it to your custom method.
For that to work, you need js to dynamically change the url or your form after a user has clicked on your Add to Cart button. Something like:
$(document).on("click","your_button_class",function(){
$(this).closest("your_form_class").attr("action","path_of_new_method");
});
You will also have to supply your items id by this form by adding a hidden field or something and then find that item inside controller method with that id to update its quantity.
Note: You need to call this js after your form is submitted and a new item is already created else it can trouble you.

Rails: duplicating information into another model if conditions met

I've got a list of articles with checkboxes on a form. When a box is checked, the body of the selected article is copied into one of x text areas.
Should a user want to make a change to the body of an article in the text area I need to send that change through my controller into a new Rails model (called an Edit).
I already have the record being created, I just need the application to record the changes into a new Edit record. Here's my relevant controller code:
new
def new
#template = Template.find(params[:template])
#article_count = #template.pages-1
#doc = Doc.new
#doc.template_id = #template.id
#doc.user_id = current_user.id
#articles = current_user.brand.articles
respond_to do |format|
format.html
format.json { render json: #doc }
end
end
create
def create
#doc = Doc.new(params[:doc])
respond_to do |format|
if #doc.save
#make editable version
if current_user.brand.try(:editable?)
#doc.articles.each do |article|
#edit = Edit.new
#edit.name = article.name
#edit.video = article.video
#something here to get the bodies of the text areas
#edit.article_id = article.id
#edit.doc_id = #doc.id
#edit.user_id = current_user.id
#edit.save
end
end
#doc.create_activity :create, owner: current_user
format.html { redirect_to share_url(#doc.user.ftp, #doc) }
format.json { render json: #doc, status: :created, location: #doc }
else
format.html { render action: "new" }
format.json { render json: #doc.errors, status: :unprocessable_entity }
end
end
end
And here's the code that makes the text areas in the view.
<% #article_count.times do |article| %>
<div id="edit<%= article %>" class="tab-pane <%= "active" if article == 0 %>">
<%= text_area_tag("edit[#{article}][body]") %>
</div>
<% end %>
Records are created for each article, but I can't seem to be able to save the edits from the text areas. It's kind of a nested-form arrangement. Any help at all would be definitely appreciated. Cheers!
I've solved this by breaking the form up into two pages: one handles the selection, the second the editing. Given that a Doc has many edits, this is the method I made for part two of the form:
def edit_content
#doc = Doc.find(params[:id])
if !#doc.edits.exists?
#doc.articles.reverse.each do |article|
#doc.edits.build(:body => article.body)
end
end
end
Hope that helps someone.

Resources