has_may for a vote app - ruby-on-rails

I'm new on rails and I'm trying to make my own simple vote application. I have two models:
class Product < ActiveRecord::Base
attr_accessible :description, :title, :photo
has_many :votes
has_attached_file :photo, :styles => { :medium => "300x300" }
before_save { |product| product.title = title.titlecase }
validates :title, presence: true, uniqueness: { case_sensitive: false }
validates :photo, :attachment_presence => true
end
class Vote < ActiveRecord::Base
belongs_to :product
attr_accessible :user_id
end
here is the product controller
class ProductsController < ApplicationController
http_basic_authenticate_with :name => "dhh", :password => "secret", :except => [:index]
def index
#products = Product.all
end
def indexprv
#products = Product.all
end
def show
#product = Product.find(params[:id])
end
def edit
#product = Product.find(params[:id])
end
def new
#product = Product.new
end
def create
#product = Product.new(params[:product])
if #product.save
redirect_to #product
else
render 'new'
end
end
def update
#product = Product.find(params[:id])
if #product.update_attributes(params[:product])
flash[:success] = "Producto Actualizado"
redirect_to root_path
else
render 'edit'
end
end
def destroy
Product.find(params[:id]).destroy
flash[:success] = "Producto Eliminado."
redirect_to root_path
end
end
I have many questions.
How can I show the total votes per products on my index page of products?
How can I create a button on my index product page to add a vote for a product?
I don't know how to do this and I couldn't find any tutorial o blog with a similar example.
Thanks for your help.

Your index view probably already loops through every product you have (either by a partial or by a loop). In your loop/partial do something like: (assuming the variable product has a instance of a product)
product.votes.count
to get the number of votes. To get a button to add a vote do something along the lines of:
link_to "Add Vote", new_product_vote_path(product), action: :new
A good tutorial that covers many aspects of rails is: http://ruby.railstutorial.org/chapters/beginning#top
Edit:
Your index method in your controller gives you an array of every product you have. So if you do something like this in your index view (if your using erb):
<ul>
<% #products.each do |product| %>
<li>
<%= product.title %>: <br />
Votes: <%= product.votes.count %> <br />
<%= link_to "Add Vote", new_product_vote_path(product), action: :new %>
</li>
<% end %>
</ul>
it should do what you want

Related

how can i set value from dropdown list on rails?

i am beginner on rails. I have product - brands list.
routes rb
Rails.application.routes.draw do
get 'welcome/index'
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
resources :brands do
resources :products
end
root 'welcome#index'
end
Brand.rb
class Brand < ApplicationRecord
has_many :products, dependent: :destroy
validates :title, presence: true,
length: { minimum: 2 }
end
Product.rb
class Product < ApplicationRecord
belongs_to :brand
end
products.controller
class ProductsController < ApplicationController
#before_action :set_brand
skip_before_action :verify_authenticity_token
def new
if params[:brand_id]
#brand = Brand.find(params[:brand_id])
end
#product = Product.new
end
def edit
#brand = #product.brand
#product = Product.find(params[:id])
end
def update
#brand = Brand.find(params[:brand_id])
#product = Product.find(params[:id])
#product.update(product_params)
redirect_to brand_path(#brand)
end
def create
#brand = Brand.find(params[:brand_id])
#product = #brand.products.create(product_params)
redirect_to brand_path(#brand)
end
def destroy
#brand = Brand.find(params[:brand_id])
#product = #brand.products.find(params[:id])
#product.destroy
redirect_to brand_path(#brand)
end
def update
#brand = Brand.find(params[:brand_id])
#product = #brand.products.find(params[:id])
#product.destroy
end
helper_method :update
private
def product_params
params.require(:product).permit(:name)
end
def set_brand
#brand = Brand.find(params[:brand_id])
end
end
.../products/new.html.erb
<h1>Add a new product</h1>
<%= form_with(model: [ #brand, #brand.products.build ], local: true) do |form| %>
<p>
<%= form.label :name,"Product name: " %><br>
<%= form.text_field :name %>
</p>
<p>
</p>
<%= form.label :title,"Select a Brand" %>
<%= form.collection_select(:brand_id, Brand.all, :id, :title,{selected: #brand.id}) %>
<p>
<%= form.submit "Add a product" %>
</p>
<% end %>
new.html.erb picture
so i want to set brand_id from selected item on dropdown list. This case, i select first item for brand_id but i cant change the brand_id. How can i set brand_id which is selected from dropdown list ? and how can i save it.
You forgot to permit the parameter in product_params. Which should be:
params.require(:product).permit(:name, :brand_id)
Unpermitted parameters are ignored by the create and update methods.
Since you already have setup products as a nested resource you neither need or want that select. The brand_id param will be passed through the path (the action attribute of the form). The user would choose what brand he wants to add a product to by what link he clicked to get to the new form.
While you could add a select to the form its a major WTF moment when you send:
POST /brands/1/products, { products: { brand_id: 5 }}
And it ends up creating a product that does not belong to Brand 1. If the param from the form was blank you would also get a really strange result.
If really wanted a form where the user selects the "target" brand on the form itself you would create a non-nested respresentation:
<%= form_with(model: #product) do |f| %>
<%= f.collection_select :brand_id %>
<% end %>
class ProductsController < ApplicationController
# GET /products/new
def new
#product = Product.new
end
# POST /products
def create
#product = Product.new(product_params)
if #product.save
redirect_to #product
else
render :new
end
end
def product_params
params.require(:product).permit(:foo, :bar, :brand_id)
end
end

How to implement Update and destroy methods with a has many through association? Rails 5

Hi I'm having trouble by making the update and destroy method in my posts_controller, I'm able to create new Posts but I'm not able to update and I want to know how to destroy the model while destroying all the associations with the other model it.
My models:
Post Model
class Post < ApplicationRecord
has_many :comments, dependent: :destroy
has_many :has_categories
has_many :categories, through: :has_categories
validates :title, presence: true,
length: { minimum: 5 }
after_create :save_categories
def categories=(value)
#categories = value
end
private
def save_categories
#categories.each do |category_id|
HasCategory.create(category_id: category_id, post_id: self.id)
end
end
end
Has_Category model
class HasCategory < ApplicationRecord
belongs_to :post
belongs_to :category
end
Category Model
class Category < ApplicationRecord
validates :name, presence: true
has_many :has_categories
has_many :posts, through: :has_categories
end
So in my partial form for the new and the edit actions is like this
<%= form_with model: #post, local: true do |form| %>
<!--Inputs before the categories-->
<div>
<label>Categories</label>
<% #categories.each do |category| %>
<div>
<%= check_box_tag "categories[]", category.id %> <%= category.name %>
</div>
<% end %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
My posts_controller create and update method
def create
#post = Post.new(post_params)
#post.categories = params[:categories]
if #post.save
redirect_to #post
else
render :new
end
end
def update
#post = Post.find(params[:id])
#post.categories = params[:categories]
if #post.update(post_params)
redirect_to #post
else
render :edit
end
end
My create action is working but the update action is just updating the inputs before the check_box_tag.
I know that the save_categories method on my Post model is the one who is taking the array I'm receiving from the form and creating the HasCategory association, How should I make the update action or even the destroy action given the situation that Is a many to many association?
The line has_many :categories, through: :has_categories gives you category_ids for post. So you can change your form:
<%= form_with model: #post, local: true do |form| %>
<!--Inputs before the categories-->
<div>
<label>Categories</label>
<%= f.collection_check_boxes(:category_ids, #categories, :id, :name)
</div>
<div>
<%= form.submit %>
</div>
<% end %>
and controller:
def create
# you need it here for correct rerendering `new` on validation error
#categories = Category.all # or maybe you have here more complicated query
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render :new
end
end
def update
# you need it here for correct rerendering `edit` on validation error
#categories = Category.all # or maybe you have here more complicated query
#post = Post.find(params[:id])
if #post.update(post_params)
redirect_to #post
else
render :edit
end
end
private
def post_params
params.require(:post).permit(:name, :some_other_post_params, category_ids: [])
end
You need to remove callback and categories=(value) method from the Post model. And define #categories in the new and edit actions. If it equals Category.all you can just put it to the form: f.collection_check_boxes(:category_ids, Category.all, :id, :name), without defining #variable

Create website and child page simultaneously in Rails 5

On my homepage, I'm trying to set it up so when you click the "Get started" button, a website record is created, but also a page belonging to that website is created, and you're redirected to the page.
This is what I have so far. The website record is being created but the page is not being created.
Models
class Page < ApplicationRecord
belongs_to :website
end
class Website < ApplicationRecord
has_many :pages, :dependent => :destroy
accepts_nested_attributes_for :pages
end
Homepage controller
class MarketingPagesController < ApplicationController
def home
#website = Website.new
#website.pages.build
end
end
Website controller
class WebsitesController < ApplicationController
def create
#website = Website.new(creation_params)
if #website.save
redirect_to #website.Page.first
else
render :new
end
end
private
def shared_params
[:name]
end
def creation_params
params.require(:website).permit(*shared_params)
end
def update_params
params.require(:website).permit(*shared_params)
end
end
Page Controller
class PagesController < ApplicationController
def create
#page = Page.new(creation_params)
if #page.save
redirect_to #page
else
render :new
end
end
def show
#page = Page.find(params[:id])
#templates = Template.all
end
private
def shared_params
[:name, :website_id]
end
def creation_params
params.require(:page).permit(*shared_params)
end
def update_params
params.require(:page).permit(*shared_params)
end
end
Website form on homepage
<%= form_for #website do |f| %>
<%= f.hidden_field :name, value: "Untitled site" %>
<%= f.fields_for :pages do |builder| %>
<%= builder.hidden_field :name, value: "Untitled page" %>
<% end %>
<%= f.submit "Create Website" %>
<% end %>
You are using the association incorrectly
# Change
#website.Page.first
# to
#website.pages.first
Change this snippet in WebsiteController
if #website.save
redirect_to #website.pages.first
else
render :new
end
you have not white listed page params in website controller. modify your shared_params in website controller to :
def shared_params
[:name, pages_attributes: [:id, :name]]
end
and of course do changes suggested by #Deepak

Rails 3.2.13 undefined method `photos_path' with nested resources with CarrierWave

I was hoping someone can give me some insights on how nested resources and more specifically how to tie it with CarrierWave.
I'm getting an error undefined method 'photos_path' and am a bit stuck on how do go about getting this to work.
I want to be able to have the following urls
Create a new photo
site.com/animal/dog/photos/new
Show photo
site.com/animal/dog/photos/my-dog-in-the-park
Should I be using a nested form? any help is much appreciated.
My Routes
root to: "home#index"
resources :animal do
resources :photos
end
My home view
<%= link_to "Add Dog Photo", new_animal_photo_path(animal_permalink: "dog") %>
My _form partial
<%= form_for [#animal, #photo], :html => {:multipart => true} do |f| %>
<%= f.hidden_field :animal_permalink %>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<p>
<%= f.file_field :image %>
</p>
<p>
</p>
<p><%= f.submit %></p>
<% end %>
My Photo model
class Photo < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
before_create :set_permalink
before_update :set_permalink
belongs_to :dog
mount_uploader :image, PhotoUploader
def set_permalink
self.permalink = title.parameterize
end
def to_param
permalink.parameterize
end
end
My Animal model
class Animal < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
has_many :photos
scope :dog, where(name: "Dog")
def to_param
permalink.parameterize
end
end
My PhotoController
class PhotosController < ApplicationController
def show
#photo = Photo.find(params[:id])
end
def new
#animal = Animal.find_by_permalink(params[:id])
#photo = Photo.new
end
def create
#photo = Photo.new(photo_params)
if #photo.save
flash[:notice] = "Successfully created photo."
redirect_to root_url
else
render :action => 'new'
end
end
def edit
#photo = Photo.find(params[:id])
end
def update
#photo = Photo.find(params[:id])
if #photo.update_attributes(photo_params)
flash[:notice] = "Successfully updated photo."
redirect_to root_url
else
render :action => 'edit'
end
end
def destroy
#photo = Photo.find(params[:id])
#photo.destroy
flash[:notice] = "Successfully destroyed photo."
redirect_to root_url
end
private
def photo_params
params.require(:photo).permit(:title, :image, :animal_id)
end
end
Thanks for taking a look. Any help is much appreciated.
I've made a few adjustments to your code.
Notice I've added an "s" to animal
root to: "home#index"
resources :animals do
resources :photos
end
notice I've removed an "s" from animals. I've changed animal_permalink to id because that is the default that would be expected by a nested resource. Also, note that, in your photos controller new method you check for the "id" param, not the animal_permalink param.
new_animal_photo_path(id: "dog")
In your _form. set the value of the animal_permalink
<%= f.hidden_field :animal_permalink, value: #animal.permalink %>
This assumes that Photo will be able to recognize the animal_permalink attribute and that you've defined an animal.permalink method/attribute. I'm not familiar with the permalink approach, so you may have to fiddle around a bit, but this should steer you in the right direction (I would normally set the :dog_id/:animal_id attribute).
Let me know how that goes.

what does it mean when the text "Asset" is getting displayed on your RoR website?

I tried to implement a commenting system on my Ruby on Rails website.
I basically followed these guidelines from this thread: Micropost's comments on users page (Ruby on Rails)
However, the comment doesn't show when I post and the text "Asset" is displayed on top of every comment box. Where is this coming from?
Updated with codes:
I am using three models to try to get the comments working as shown in the above link
user.rb
class User < ActiveRecord::Base
has_many :microposts, dependent: :destroy
has_many :comments
micropost.rb
class Micropost < ActiveRecord::Base
attr_accessible :content, :image, :comment_content
belongs_to :user
has_many :comments, dependent: :destroy
validates :user_id, presence: true
validates :content, presence: true
default_scope order: 'microposts.created_at DESC'
def self.from_users_followed_by(user)
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
where("user_id IN (#{followed_user_ids}) OR user_id = :user_id",
user_id: user.id)
end
end
comment.rb
class Comment < ActiveRecord::Base
attr_accessible :comment_content
belongs_to :user
belongs_to :micropost
validates :comment_content, presence: true
validates :user_id, presence: true
validates :micropost_id, presence: true
end
comments controller
class CommentsController < ApplicationController
def create
#micropost = Micropost.find(params[:micropost_id])
#comment = Comment.new(params[:comment])
#comment.micropost = #micropost
#comment.user = current_user
if #comment.save
redirect_to(:back)
else
render 'shared/_comment_form'
end
end
end
micropost controller
class MicropostsController < ApplicationController
before_filter :signed_in_user
before_filter :correct_user, only: :destroy
def create
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Posted"
redirect_to root_path
else
#feed_items = []
render 'static_pages/home'
end
end
def destroy
#micropost.destroy
redirect_to root_path
end
private
def correct_user
#micropost = current_user.microposts.find_by_id(params[:id])
redirect_to root_path if #micropost.nil?
end
end
user controller
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
#comment = Comment.new
end
comment form
<%= form_for([micropost, #comment]) do |f| %>
routes.rb
resources :microposts do
resources :comments
end
micropost view
<li>
<span class="content"><%= simple_format(micropost.content) %></span>
<%= image_tag micropost.image_url(:thumb).to_s %><br>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
<%= render 'shared/comment_form', micropost: micropost %>
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
confirm: "You sure?",
title: micropost.content %><br>
<% end %>
</li>
Maybe you have some static informations in one of your partials or in the form control that render your textarea for the comment?
Check you view for the controller or maybe it is somewhere in the 'static_pages/home'
EDIT:
Just full search your project for the text asset, maybe you find an image alt="assets" tag for an image and the image is not available.

Resources