Undefined method 'id' for #ActiveRecord::Relation - ruby-on-rails

I am having trouble debugging this error. NoMethodError in Products#Index undefined method 'id' for #ActiveRecord::Relation
Here is my products controller:
class ProductsController < ApplicationController
def index
if params[:query].present?
#products = Product.search_by_name_and_category(params[:query])
else
#products = Product.all
end
end
def new
#product = Product.new
#product.user = current_user
end
def show
#product = Product.find(params[:id])
end
end
Here is my product model:
class Product < ApplicationRecord
belongs_to :user
has_many :bookings
validates :name, presence: true
validates :category, presence: true
has_one_attached :photo
include PgSearch::Model
pg_search_scope :search_by_name_and_category,
against: [ :name, :category ],
using: {
tsearch: { prefix: true } # <-- now `superman batm` will return something!
}
end
This is my product-card partial.
<div class="card-product-container">
<div class="cards">
<div class="card-product">
<%= link_to product_path(#products.id) do %>
<img src="https://source.unsplash.com/random/?<%= product.name %>" />
<div class="card-product-footer">
<div>
<h2><%= product.name %></h2>
<p><%= product.category %></p>
</div>
<h2><%= product.price %></h2>
</div>
<% end %>
</div>
</div>

#products is a list of many Products. The list doesn't have an ID. Each Product in the list does.
You instead want the ID of a each individual Product in #products.
Iterate through #products and work with a single Product.
<% #products.each do |product| %>
<div class="card-product">
<%= link_to product_path(product.id) do %>
<img src="https://source.unsplash.com/random/?<%= product.name %>" />
<% end %>
<div class="card-product-footer">
<div>
<h2><%= product.name %></h2>
<p><%= product.category %></p>
</div>
<h2><%= product.price %></h2>
</div>
</div>
<% end %>

Related

One to many relationship: Couldn't find Ofertum with 'id'=3

I have problem to link to show file
http://localhost:3000/sigs/3
I got this error
ActiveRecord::RecordNotFound in SigsController#show
Couldn't find Ofertum with 'id'=3
Please help me. Here is my codes
I have two models: One for offers and one for signs
Each offer have signs and each sign have one offer
In sig model
belongs_to :ofertum
in ofertum model
has_many :sigs
in routes
resources :oferta
resources :sigs
in sigs controller
class SigsController < ApplicationController
def new
#ofertum = Ofertum.find(params[:id])
#sig = #ofertum.sigs.build
end
def create
#ofertum = Ofertum.find(params[:id])
#sig = #ofertum.sigs.build(sig_params)
if #sig.save
redirect_to sigs_path(:id => #ofertum.id)
end
end
def destroy
#ofertum = Ofertum.find(params[:id])
#sigs=#ofertum.sigs
if #sigs.destroy
redirect_to root_path
end
end
def show
#ofertum = Ofertum.find(params[:id])
#sigs=#ofertum.sigs
end
def edit
end
def update
end
def index
end
def destroy
end
private
def sig_params
sig_params = params.require(:sig).permit([:name,:comment,:description,:price,:image])
end
end
In oferta index.html.erb
<% #oferta.each do |o| %>
<ul class="cat">
<li class="pull-left"><h2><%= link_to o.offer,o %></h2><br><h4><%= o.description %></h4>
<div class="main">
<% if o.sigs.exists? %>
<div id="myBtnContainer">
<% for item in o.sigs %>
<button class="btn active" onclick="filterSelection('<%= item.name %>')"><%= item.name%><%= link_to item.description,item %><br></button>
<% end %>
</div>
<div class="row">
<% for item in o.sigs %>
<div class="column <%= item.name %>">
<div class="content">
<%= image_tag item.image.url(), style: "width:100%"%>
<h4><br><%= item.name %></h4>
<p><%= item.comment %></p>
</div>
</div>
<% end %><br><br>
</div>
<% end %>
</div>
<% end %>
In the database
class AddNameToSig < ActiveRecord::Migration[5.2]
def change
add_column :sigs, :name, :string
add_column :sigs, :comment, :string
add_column :sigs, :description, :text
add_column :sigs, :price, :string
end
end
in sigs show.html.erb
<%= #sig.name %>
<div class="container">
<%= link_to #sig, class: "btn tf-btn btn-default", method: :delete, data: { confirm: "Are you sure you want to delete this sign?" } do %>Delete <% end %>
</div>
The param in the path /sigs/3 is about a sig, not an ofertum. So you might do this way
def show
sig = Sig.find(params[:id])
#sigs = sig.ofertum.sigs
end
or
def show
ofertum = Sig.find(params[:id]).ofertum
#sigs = ofertum.sigs
end
and yes, if you need even the ofertum in the view
def show
#ofertum = Sig.find(params[:id]).ofertum
#sigs = #ofertum.sigs
end

How to display image in index view

I am using carrierwave and trying to display images of products in the index view. This are my models, controllers and views
product.rb
class Product < ActiveRecord::Base
has_many :order_items
belongs_to :category, required: false
has_many :product_attachments
accepts_nested_attributes_for :product_attachments
mount_uploader :image, ImageUploader
default_scope { where(active: true) }
end
product_attachment.rb
class ProductAttachment < ApplicationRecord
mount_uploader :image, ImageUploader
belongs_to :product
end
products_controller.rb (extract)
class ProductsController < ApplicationController
def index
#products = Product.all
#order_item = current_order.order_items.new
end
def show
#product = Product.find(params[:id])
#product_attachments = #product.product_attachments.all
end
def new
#product = Product.new
#product_attachment = #product.product_attachments.build
#categories = Category.all.map{|c| [ c.name, c.id ] }
end
def create
#product = Product.new(product_params)
#product.category_id = params[:category_id]
respond_to do |format|
if #product.save
params[:product_attachments]['image'].each do |a|
#product_attachment = #product.product_attachments.create!(:image => a, :product_id => #product.id)
end
format.html { redirect_to #product, notice: 'Product was successfully created.' }
else
format.html { render action: 'new' }
end
end
end
private
def product_params
params.require(:product).permit(:name,:price, :active, :description, product_attachments_attributes:
[:id, :product_id, :image], category_attributes: [:category_id, :category])
end
end
index.html.erb
<div class="row">
<div class="col-xs-offset-1 ">
<% #products.each do |product| %>
<%= render "product_row", product: product, order_item: #order_item %>
<% end %>
</div>
_product_row.html.erb
<div class="well">
<div class="row">
<div class="container-fluid row">
<div class="col-md-5 col-lg-5 col-sm-5 col-xs-5 binder">
<br><%= image_tag product.image_url.to_s %><br><br>
</div>
<div class="col-md-4 col-lg-4 col-sm-4 col-xs-4 binder">
<h4 class="text-left"><%= product.name.split.map(&:capitalize).join(' ') %> </h4>
<h4 class="text-left"><span style="color: green"><%= number_to_currency(product.price, :unit => "€") %></span></h4>
<h4><%= link_to Category.find(product.category_id).name, category_path(product.category_id) %></h4>
<h6 class="text-left"><%= link_to 'Delete', product_path(product), method: :delete,
data: { confirm: 'Are you sure?' } %></h6><br><br>
</div>
<div class="col-md-3 col-lg-3 col-sm-3 col-xs-3 binder">
<%= form_for order_item, remote: true do |f| %>
<div class="input-group">
<%= f.number_field :quantity, value: 1, class: "form-control", min: 1 %>
<div class="input-group-btn">
<%= f.hidden_field :product_id, value: product.id %>
<%= f.submit "Add to Cart", class: "btn btn-primary text-right" %>
</div>
</div>
<% end %>
</div>
</div>
</div>
</div>
With <%= image_tag product.image_url.to_s %> the image doesn't appear. When I change it to <%= image_tag product_attachments.first.image_url.to_s %> I get the following error:
undefined local variable or method `product_attachments' for #<#<Class:0x00007f28544ccc68>:0x00007f285dd3aab8>
I am pretty new to Ruby and don't know what I am doing wrong or how to fix this. Any help would be appreciated. I am using Ruby version 2.5.1 and rails 5.2.0 on ubuntu.
I would expect that the following works:
<%= image_tag product.product_attachments.first.image_url.to_s %>
Try this:
image_tag(product.product_attachments.first.image.url.to_s)
It should work. I realized that sometimes image_url doesn't work as expected but image.url does.
If i understand well your snippets, the model you mount the ImageUploader is ProductAttachment (which have the attribute image) so you can remove mount_uploader :image, ImageUploader of your Product model.
The image is mounted on every product_attachments for one product. Just display the images inside the partial by iterating through product_attachments:
<% product.product_attachments.each do |attachment| %>
<%= image_tag(attachment.image.url) %>
<% end %>

How to show validation errors for each element of a collection?

I am modifying the blog of the guide that I have just finished.
What I want now is to show if there were errors in validating comments in the same way that I show them when validating articles.
I already established the corresponding restrictions in the model, and in fact works, because when you want to comment with values that violate the restriction, the system does not save the comment, but the user does not know why that happens.
Articulo means an article
Comentar means to comment
Comentario means a comment
Comentarios means comments
Annex some files:
/app/controllers/articulos_controller.rb
class ArticulosController < ApplicationController
http_basic_authenticate_with name: "dhh", password: "secreto", except: [:index, :show]
def index
#articulos = Articulo.all
end
def show
#articulo = Articulo.find(params[:id])
end
def new
#articulo = Articulo.new
end
def edit
#articulo = Articulo.find(params[:id])
end
def create
#articulo = Articulo.new(articulo_params)
if #articulo.save
redirect_to #articulo
else
render 'new'
end
end
def update
#articulo = Articulo.find(params[:id])
if #articulo.update(articulo_params)
redirect_to #articulo
else
render 'edit'
end
end
def destroy
#articulo = Articulo.find(params[:id])
#articulo.destroy
redirect_to articulos_path
end
private
def articulo_params
params.require(:articulo).permit(:titular, :contenido)
end
end
/app/controllers/comentarios_controller.rb
class ComentariosController < ApplicationController
http_basic_authenticate_with name: "dhh", password: "secreto", only: :destroy
def new
articulo = Articulo.find(params[:id])
#comentario = articulo.comentarios.build
end
def create
#articulo = Articulo.find(params[:articulo_id])
#comentario = #articulo.comentarios.create(comentario_params)
redirect_to articulo_path(#articulo)
end
def destroy
#articulo = Articulo.find(params[:articulo_id])
#comentario = #articulo.comentarios.find(params[:id])
#comentario.destroy
redirect_to articulo_path(#articulo)
end
private
def comentario_params
params.require(:comentario).permit(:comentarista, :contenido)
end
end
/app/models/articulo.rb
class Articulo < ApplicationRecord
has_many :comentarios
validates :titular, presence: true, length: { minimum: 5 }
validates :contenido, presence: true, length: { minimum: 10 }
end
/app/models/comentario.rb
class Comentario < ApplicationRecord
belongs_to :articulo
validates :comentarista, presence: true, length: { minimum: 4 }
validates :contenido, presence: true, length: { minimum: 2 }
end
/app/views/articulos/_form.html.erb
<%= form_for #articulo do |f| %>
<% if #articulo.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#articulo.errors.count, "error") %> han impedido que el artículo sea grabado:
</h2>
<ul>
<% #articulo.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :titular %><br>
<%= f.text_field :titular %>
</p>
<p>
<%= f.label :contenido %><br>
<%= f.text_area :contenido %>
</p>
<p>
<%= f.submit 'Guardar' %>
</p>
<% end %>
/app/views/comentarios/_form.html.erb
<%= form_for([#articulo, #articulo.comentarios.build]) do |f| %>
<p>
Nombre<br>
<%= f.text_field :comentarista %>
</p>
<p>
Comentario<br>
<%= f.text_area :contenido %>
</p>
<p>
<%= f.submit 'Comentar' %>
</p>
<% end %>
How can I be able to show such errors when validating?
From already thank you very much.
You should create an error messages partial app/views/shared/_error_messages.html.erb like so:
<% if object.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(object.errors.count, "error") %> han impedido que el
artículo sea grabado: # change artículo to the appropriate word
</h2>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
And render the error messages partial inside your articles and comments forms:
/views/comentarios/_form.html.erb and
/views/articulos/_form.html.erb
<%= render 'shared/error_messages', object: f.object %>
This is the DRY way and it'll work with both articles and comments or any object.
Also, change your comments controller's create action to this as it redirects to the article show page instead of showing the errors:
def create
#articulo = Articulo.find(params[:articulo_id])
#comentario = #articulo.comentarios.create(comentario_params)
if #comentario.save
redirect_to articulo_path(#articulo)
end
end

undefined method `message_time' for nil:NilClass ruby on rails

I am receiving undefined method on `message_time' for nil:NilClass:
Showing /home/ubuntu/workspace/app/views/conversations/index.html.erb where line #19 raised:
undefined method `message_time' for nil:NilClass
Extracted source (around line #19):
17
18
19
20
21
22
<div class="col-md-2">
<%= other.fullname %><br>
19 <%= conversation.messages.last.message_time %>
</div>
<div class="col-md-8">
<%= conversation.messages.last.content %>
app/views/conversations/index.html.erb
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">Your conversations</div>
<div class="panel-body">
<div class="container">
<% #conversations.each do |conversation| %>
<% other = conversation.sender == current_user ? conversation.recipient : conversation.sender %>
<%= link_to conversation_messages_path(conversation) do %>
<div class="row conversation">
<div class="col-md-2">
<%= image_tag avatar_url(other), class: "img-circle avatar-medium" %>
</div>
<div class="col-md-2">
<%= other.fullname %><br>
<%= conversation.messages.last.message_time %>
</div>
<div class="col-md-8">
<%= conversation.messages.last.content %>
</div>
</div>
<% end %>
<% end %>
</div>
</div>
</div>
<h3>All Users</h3>
<% #users.each do |user| %>
<% if user != current_user %>
<%= user.fullname %>
<%= link_to "Send Message", conversations_path(sender_id: current_user.id, recipient_id: user.id), method: 'post' %>
<% end %>
<br>
<% end %>
</div>
Conversation controller
class ConversationsController < ApplicationController
before_action :authenticate_user!
def index
#users = User.all
#conversations = Conversation.involving(current_user)
end
def create
if Conversation.between(params[:sender_id], params[:recipient_id]).present?
#conversation = Conversation.between(params[:sender_id], params[:recipient_id]).first
else
#conversation = Conversation.create(conversation_params)
end
redirect_to conversation_messages_path(#conversation)
end
private
def conversation_params
params.permit(:sender_id, :recipient_id)
end
end
Conversation.rb
class Conversation < ActiveRecord::Base
belongs_to :sender, foreign_key: :sender_id, class_name: 'User'
belongs_to :recipient, foreign_key: :recipient_id, class_name: 'User'
has_many :messages, dependent: :destroy
validates_uniqueness_of :sender_id, scope: :recipient_id
scope :involving, -> (user) do
where("conversations.sender_id = ? OR conversations.recipient_id = ?", user.id, user.id)
end
scope :between, -> (sender_id, recipient_id) do
where("(conversations.sender_id = ? AND conversations.recipient_id = ?) OR (conversations.sender_id = ? AND conversations.recipient_id = ?)",
sender_id, recipient_id, recipient_id, sender_id)
end
end
I think conversation has no messages and so conversation.messages.last is nil.
You are calling message_time on a nil object and that's what the error message says. Before calling message_time, make sure converation.messages is not empty. You could solve this by
<% messages = conversation.messages %>
<%= messages.last.message_time unless messages.empty? %>
Other option is
<% messages = conversation.messages %>
<%= messages.last.try(:message_time) %>

How to Order Nested Attribute according to its Date?

How can we DESC order results according to its :date_value in the quantifieds index?
Results being the nested attribute to quantifieds.
Right now the order is according to where the User added the result in the form, regardless of :date_value.
This has proven more difficult than I would have guessed.
class QuantifiedsController < ApplicationController
before_action :set_quantified, only: [:show, :edit, :update, :destroy]
before_action :logged_in_user, only: [:create, :destroy]
def index
if params[:tag]
#quantifieds = Quantified.tagged_with(params[:tag])
else
#quantifieds = Quantified.joins(:results).all
#averaged_quantifieds = current_user.quantifieds.averaged
#instance_quantifieds = current_user.quantifieds.instance
end
end
def show
end
def new
#quantified = current_user.quantifieds.build
end
def edit
end
def create
#quantified = current_user.quantifieds.build(quantified_params)
if #quantified.save
redirect_to quantifieds_url, notice: 'Quantified was successfully created'
else
#feed_items = []
render 'pages/home'
end
end
def update
if #quantified.update(quantified_params)
redirect_to quantifieds_url, notice: 'Goal was successfully updated'
else
render action: 'edit'
end
end
def destroy
#quantified.destroy
redirect_to quantifieds_url
end
private
def set_quantified
#quantified = Quantified.find(params[:id])
end
def correct_user
#quantified = current_user.quantifieds.find_by(id: params[:id])
redirect_to quantifieds_path, notice: "Not authorized to edit this goal" if #quantified.nil?
end
def quantified_params
params.require(:quantified).permit(:categories, :metric, :result, :date, :tag_list, results_attributes: [:id, :result_value, :date_value, :_destroy])
end
end
class Quantified < ActiveRecord::Base
belongs_to :user
has_many :results #correct
accepts_nested_attributes_for :results, :reject_if => :all_blank, :allow_destroy => true #correct
scope :averaged, -> { where(categories: 'Averaged') }
scope :instance, -> { where(categories: 'Instance') }
validates :categories, :metric, presence: true
acts_as_taggable
CATEGORIES = ['Averaged', 'Instance']
end
class Result < ActiveRecord::Base
belongs_to :user
belongs_to :quantified
end
class CreateQuantifieds < ActiveRecord::Migration
def change
create_table :quantifieds do |t|
t.string :categories
t.string :metric
t.references :user, index: true
t.timestamps null: false
end
add_foreign_key :quantifieds, :users
add_index :quantifieds, [:user_id, :created_at]
end
end
class CreateResults < ActiveRecord::Migration
def change
create_table :results do |t|
t.string :result_value
t.date :date_value
t.integer :quantified_id
t.timestamps null: false
end
end
end
form
<%= javascript_include_tag "quantified.js" %>
<%= simple_form_for(#quantified) do |f| %>
<%= f.error_notification %>
<div class="america">
<form>
<% Quantified::CATEGORIES.each do |c| %>
<%= f.radio_button(:categories, c, :class => "date-format-switcher") %>
<%= label(c, c) %>
<% end %>
<br/>
<br/>
<div class="form-group">
<%= f.text_field :tag_list, quantified: #quantified.tag_list.to_s.titleize, class: 'form-control', placeholder: 'Enter Action' %>
</div>
<div class="form-group">
<%= f.text_field :metric, class: 'form-control', placeholder: 'Enter Metric' %>
</div>
<div id="results">
<%= f.fields_for :results do |result| %>
<%= render 'result_fields', :f => result %>
<% end %>
</div>
<div class="links">
<b><%= link_to_add_association 'Add Result', f, :results %></b>
</div>
<div class="america2">
<%= button_tag(type: 'submit', class: "btn") do %>
<span class="glyphicon glyphicon-plus"></span>
<% end %>
<%= link_to quantifieds_path, class: 'btn' do %>
<span class="glyphicon glyphicon-chevron-left"></span>
<% end %>
<%= link_to #quantified, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn' do %>
<span class="glyphicon glyphicon-trash"></span>
<% end %>
</div>
</form>
</div>
<% end %>
index
<!-- Default bootstrap panel contents -->
<div id="valuations" class="panel panel-default">
<div class="panel-heading"><h4><b>AVERAGE</b></h4></div>
<% #averaged_quantifieds.each do |averaged| %>
<div class="attempt">
<b><%= raw averaged.tag_list.map { |t| link_to t.titleize, tagquantifieds_path(t) }.join(', ') %>
<%= link_to edit_quantified_path(averaged) do %>
(<%= averaged.metric %>)</b>
<% end %>
<ul>
<% averaged.results.each do |result| %>
<li>
<b><%= result.result_value %></b>
<%= result.date_value.strftime("%b %Y") %>
</li>
<% end %>
</ul>
</div>
<% end %>
</div>
<div class="valuations-button">
<%= link_to new_quantified_path, class: 'btn' do %>
<b><span class="glyphicon glyphicon-plus"</span></b>
<% end %>
</div>
<br>
<!-- Default bootstrap panel contents -->
<div id="valuations" class="panel panel-default">
<div class="panel-heading"><h4><b>INSTANCE</b></h4></div>
<% #instance_quantifieds.each do |instance| %>
<div class="attempt">
<b><%= raw instance.tag_list.map { |t| link_to t.titleize, tagquantifieds_path(t) }.join(', ') %>
<%= link_to edit_quantified_path(instance) do %>
(<%= instance.metric %>)</b>
<% end %>
<ul>
<% instance.results.each do |result| %>
<li>
<%= result.date_value.strftime("%b.%d.%y") %>
<%= result.result_value %>
</li>
<% end %>
</ul>
</div>
<% end %>
</div>
<div class="valuations-button">
<%= link_to new_quantified_path, class: 'btn' do %>
<b><span class="glyphicon glyphicon-plus"</span></b>
<% end %>
</div>
Thanks so much for your time!
Got it! Add default_scope { order('date_value DESC') } in result.rb

Resources