Rails has_many and belongs to twice in single view - ruby-on-rails

In my database a sock has_many shoes. A shoe has_many shoelaces.
create_table "socks", force: :cascade do |t|
t.string "sock_name"
end
create_table "shoes", force: :cascade do |t|
t.integer "sock_id"
t.string "shoe_name"
end
create_table "shoelaces", force: :cascade do |t|
t.integer "shoe_id"
t.integer "shoelace_name"
end
In the socks view I want to display all the shoes a sock owns, and all shoelaces a shoe owns.
In my controller I have:
def show
#sock = Sock.find(params[:id])
#shoes = #sock.shoes
#shoelaces = Shoelace.where(shoe: #shoes)
end
I'm running something like this now:
<% #shoes.each do |shoe| %>
<%= shoe.shoe_name %>
<%= Shoelace.where(shoe: shoe).shoelace_name %>
<% end %>
How can I display my shoelaces belonging to my shoes inside of a .each so that they are together in my view without have a query outside of the controller? Would this method apply if my shoelace has_many strings?

You can have something in your Shoe model such as:
def shoelaces_for_shoe
Shoelace.where(shoe_id: id).shoelace_name
end
Then in your view:
<% #shoes.each do |shoe| %>
<%= shoe.shoe_name %>
<%= #shoe.shoelaces_for_shoe %>
<% end %>

Related

Rails Access data from a different Model within a Controller

UPDATE: Problem solved, thanks to Sebastian and Gabriel for the helpful pointers.
The relevant changes to my code are as follows:
app/controllers/pomodoro_cycles_controller.rb
def pomodoro_collections
{
pomodoro_collection_0: Pomodoro.offset(0).first(100),
pomodoro_collection_1: Pomodoro.offset(100).first(100)
}
end
app/views/pomodoro_cycles/show.html.erb
<% #pomodoros_collections.each do |pomodoros_collection_hash| %>
<h2><%= pomodoros_collection_hash[0] %></h2>
<% pomodoros_collection_hash[1].each do |pomodoro| %>
<p>
<%= pomodoro.id %>
<%= pomodoro.color %>
</p>
<% end %>
<% end %>
NOTA BENE:
The #first method in ActiveRecord returns an Array, so the keys in my original Hash were nested Arrays. Instead, the following was sufficient to return an Array of Pomodoro objects:
Pomodoro.offset(0).first(100)
DESCRIPTION OF ORIGINAL PROBLEM
Rails 5, PostgreSQL
PROBLEM: I cannot access Pomodoro.all from within PomodoroCycleController
I have two scaffolds: Pomodoro and PomodoroCycle, and I want to access the full list of Pomodoros within the PomdoroCycle controller.
The following code is kept simple, in order to make as clear as possible what I'm trying to do. If I can do these things, then I'll be able to do much more, but one step at a time.
Regarding the db migration files, I have already run bundle exec rails db:migrate
I want to display a full list of Pomodoros in the PomodoroCycle Show View (later to be displayed in Index), but I don't know what is missing.
From app/controllers/pomodoro_cycles_controller.rb
def show
#pomodoros_collections = pomodoro_collections
end
def pomodoro_collections
{
pomodoro_collection_0 => [Pomodoro.offset(0).first(100)],
pomodoro_collection_1 => [Pomodoro.offset(100).first(100)]
}
end
From app/views/pomodoro_cycles/show.html.erb
<% #pomodoros_collections.each do |collection| %>
<p><%= collection %></p>
<% end %>
However, this displays nothing in the browser.
app/models/pomodoro_cycle.rb
class PomodoroCycle < ApplicationRecord
has_many :pomodoros
end
app/models/pomodoro.rb
class Pomodoro < ApplicationRecord
belongs_to :pomodoro_cycle
end
Updated db/migrate/20180103032759_create_pomodoro_cycles.rb:
class CreatePomodoroCycles < ActiveRecord::Migration[5.1]
def change
create_table :pomodoro_cycles do |t|
t.string :activity
t.integer :iteration
t.integer :matrix_side_length
t.datetime :created_at
t.datetime :completed_at
t.string :category_labels, array:true, default: []
t.string :category_colors, array:true, default: []
t.string :username
t.timestamps
end
create table :pomodoros do |t|
t.belongs_to :pomodoro_cycle, index: true
t.datetime :completed_at
t.timestamps
end
add_index :pomodoros, :pomodoro_cycle_id
end
end
Untouched db/migrate/20180103054425_create_pomodoros.rb
class CreatePomodoros < ActiveRecord::Migration[5.1]
def change
create_table :pomodoros do |t|
t.boolean :status
t.string :category
t.string :color
t.datetime :completed_at
t.string :username
t.timestamps
end
end
end
First of all, as #SebastianPalma pointed out in the comments, the syntax is wrong
def pomodoro_collections
{
pomodoro_collection_0 => [Pomodoro.offset(0).first(100)],
pomodoro_collection_1 => [Pomodoro.offset(100).first(100)]
}
end
should be:
def pomodoro_collections
{
pomodoro_collection_0: [Pomodoro.offset(0).first(100)],
pomodoro_collection_1: [Pomodoro.offset(100).first(100)]
}
end
Make the keys in the hash symbols
Then to display each Pomodoro put something like:
<% #pomodoros_collections.each do |pomodoros_collection_hash| %>
<h2><%= pomodoros_collection_hash[0] %></h2>
<% pomodoros_collection_hash[1].each do |pomodoro| %>
<p><%= pomodoro.id %></p> #Or the attribute you want to display
<% end %>
<% end %>
Hope this help

undefined method `image' for #<Array> when using group_by

Hi can anyone point me to the right direction? I´m trying to show images on views/pages/index.html.erb the images are uploaded on views/products/new.html.erbthrough the _form.html.erbpartial. Each product/picture then belongs to a category which I can select in the _navbar.html.erb and is then directed to the views/categories/show.html.erbto see pictures of each product in that category and so on.
That is all working fine
But now I want to display the last added picture in each category on the views/pages/index.html.erb and I´m always getting this error : undefined method 'image' for #<Array:0x007f8d1fb19ff0>
I´m pretty lost at the moment, and hopefully someone can guide me to the right path.
My code id like this:
pages_controller.rb
class PagesController < ApplicationController
def index
#products = Product.all.order(created_at: :desc).group_by(&:category_id)
end
def about
end
def location
end
def stockists
end
end
views/pages/index.html.erb
<% #products.each do |product| %>
<div class="col-lg-3 col-sm-6 col-xs-12 center-block " >
<%= image_tag product.image.url(:medium) %>
<p><%= product.name %></p>
<p><%= product.category.name %></p>
<% end %>
</div>
And then I have, the products.rb and category.rb
product.rb
class Product < ActiveRecord::Base
mount_uploader :image, ImageUploader
validates_presence_of :name, :price
validates_numericality_of :price
belongs_to :category
end
category.rb
class Category < ActiveRecord::Base
has_many :products
end
this as part of the schema.rb
create_table "products", force: :cascade do |t|
t.string "name"
t.string "description"
t.float "price"
t.string "image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "category_id", default: 1
end
add_index "products", ["category_id"], name: "index_products_on_category_id", using: :btree
and in the end there is this part
add_foreign_key "order_items", "orders", on_delete: :cascade
add_foreign_key "order_items", "products"
add_foreign_key "orders", "users", on_delete: :cascade
add_foreign_key "products", "categories"
end
You are using group_by in the controller, an enumerable method that returns a hash of Product arrays keyed by category_id.
#product = {
:category1 => [#<Product category_id=1>, #<Product category_id=1>, ...],
:category2 => [#<Product category_id=2>, #<Product category_id=2>, ...]
}
When you loop through #products in the view, you are looping through a hash where each iteration is passing an array.
The product variable does not contain a product, but an array of products.
<% #products.each do |product| %> # product is type Array!
<%= image_tag product.image.url(:medium) %> # Array.image throws an error!
<% end %>
You must create an outer loop to step through the hash.
<% #products.each do |category, products| %>
<% products.each do |product| %>
# do stuff
<% end %>
<% end %>

How to manage nested relation in RoR?

I'm new to Rails and trying to create an C2C marketplace App just to understand how rails works and start some real project later on.
The idea is that User can place Order to other User. Orders is related with user's Printer (you choice user where you want to print on /order page). Printer in its turn have many colors to print
For now, Im stack with very basic things.
I want to display list of all printers with owner's name and colors available on /order page in Orders controller. Thank you in advance for any ideas how I can do this
class User < ActiveRecord::Base
has_many :printers, dependent: :destroy
has_many :orders, dependent: :destroy
end
class Order < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
end
class Printer < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
validates :model, presence: true, allow_nil: true
has_many :materials, dependent: :destroy
end
Orders controller with index action (/orders)
class OrdersController < ApplicationController
def index
#printers = Printer.all
end
end
And index.html.erb
<% #printers.each do |p| %>
<div class="col-xs-9">
<div><%= "USERNAME HERE" %></div> <!--Do not know how to display parent's model here? -->
<div><%= p.model %></div> <!--Works fine-->
<div><%= p.colors.each do |c| c.color end %></div> <!--Display array instead of strings-->
</div>
<div class="col-xs-3">
<button>Order</button>
</div>
<% end %>
Simplified schema.db
ActiveRecord::Schema.define(version: 20160627132435) do
create_table "colors", force: :cascade do |t|
t.string "color"
t.integer "printer_id"
end
create_table "orders", force: :cascade do |t|
t.string "price"
t.string "status"
t.integer "user_id"
end
create_table "printers", force: :cascade do |t|
t.string "model"
t.integer "user_id"
end
add_index "printers", ["user_id"], name: "index_printers_on_user_id", using: :btree
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
end
add_foreign_key "orders", "users"
add_foreign_key "printers", "users"
end
I think you should be able to do:
<% #printers.each do |p| %>
<div class="col-xs-9">
<div><%= p.user.first_name %></div> <!--Do not know how to display parent's model here? -->
<div><%= p.model %></div> <!--Works fine-->
<div><% p.colors.each do |c|
<%= c.color %>
<% end %></div> <!--Display array instead of strings-->
</div>
<div class="col-xs-3">
<button>Order</button>
</div>
<% end %>

Getting no method error in Customers#index while trying to access attribute of Order model

How to access customer's name from Customer table onto my Order's action view index. And vice versa.
I am getting this error:
NoMethodError in Customers#index.
Undefined method `order' for #Customer:0x24f4...
class Customer < ActiveRecord::Base
has_many :orders, foreign_key: "customer_id"
end
class Order < ActiveRecord::Base
belongs_to :customer
end
In my migration:
create_table "customers", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "orders", force: :cascade do |t|
t.integer "customer_id"
t.datetime "orderdate"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "orders", ["customer_id"], name: "index_orders_on_customer_id", using: :btree
end
In customer's index.html.erb
<% #customers.each do |customer| %>
<%= customer.name %>
<%= customer.order.orderdate %>
<% end %>
In orders's index.html.erb
<% #orders.each do |order| %>
<%= order.orderdate %>
<%= order.customer.name %>
<% end %>
you have has many relation with customer and order so
In customer's index.html.erb
<% #customers.each do |customer| %>
<%= customer.name %>
<!-- this will display customers all order and fetch first and then show that order's orderdate -->
<%= customer.orders.first.orderdate %>
<% end %>
Try a different approach to setting the relationships between your models in the migration:
t.belongs_to :customer, index: true
See http://guides.rubyonrails.org/association_basics.html#the-belongs-to-association

Search from one view through a joined HABTM table

I'm trying to set up a simple search from one view through a joined table and, being a newbie, it's not quite working. I'm testing with the word "books" which I know is in the articles table in the subject column.
My error is:
SQLite3::SQLException: no such column: articles.subject: SELECT COUNT(*) FROM "keywords" WHERE (articles.subject LIKE '%books%')
My schema is:
create_table "articles", force: true do |t|
t.string "title"
t.string "subject"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "articles_keywords", id: false, force: true do |t|
t.integer "article_id"
t.integer "keyword_id"
end
create_table "keywords", force: true do |t|
t.string "keyword"
t.datetime "created_at"
t.datetime "updated_at"
end
My model is:
class Keyword < ActiveRecord::Base
has_and_belongs_to_many :articles
accepts_nested_attributes_for :articles
def self.search_for(query)
where('articles.subject LIKE :query', :query => "%#{query}%")
end
My controller action is:
def index
#keywords = params[:q] ? Keyword.search_for(params[:q]) : Keyword.all
end
My view action is:
<%= form_tag "/", method: "GET" do %>
<%= text_field_tag :q %>
<%= submit_tag "Search" %>
<% end %>
<% if #keywords.any? %>
<% #keywords.each do |k| %>
<section>
<h3><b>Title</b>: <%= link_to k.keyword.title, keyword.title %></h3>
<p><b>Subject</b>: <%= keyword.subject%></p>
<% end %>
Many thanks for any help!
You have to join the asocciated table (and you can use scopes):
class Keyword < ActiveRecord::Base
has_and_belongs_to_many :articles
accepts_nested_attributes_for :articles
scope :search_for, ->(query){ joins(:acticles).where('articles.subject LIKE :query', :query => "%#{query}%")}
end
See RubyOnRails-Guides
A gem that eases complex querying is squeel. With squeel the scope would by
scope :search_for, ->(query){ joins{articles}.where{articles.subject =~ "%#{query}%"} }

Resources