Join table HABTM association - ruby-on-rails

I have created a join table for categories and products. Do I need a category_id column in my products database? When I go into my rails console and type Product.new there is no category attribute. If I would like to assign categories to a product how would I do this? This is my first time working with a join table and I am confused on how it works?
My tables:
create_table "categories", force: :cascade do |t|
t.string "name"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "category_id"
end
create_table "categories_products", id: false, force: :cascade do |t|
t.integer "category_id", null: false
t.integer "product_id", null: false
end
create_table "products", force: :cascade do |t|
t.string "title"
t.text "description"
t.string "image_url"
t.integer "price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "image", default: "{}"
end
My associations:
class CategoryProduct < ActiveRecord::Base
belongs_to :category
belongs_to :product
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :categories
has_many :categories_products
end
class Category < ActiveRecord::Base
has_and_belongs_to_many :products
has_many :categories_products
end
category portion of my products form:
<% for category in Category.all %>
<div>
<%= check_box_tag "product[category_ids][]", category.id, #product.categories.include?(category) %>
<%= category.name %>
</div>
<% end %>

You are already having the HABTM join table so you don't need to have category_id in products table.
you can assign categories to products like following in your rails console
product = Product.create(..)
category = Category.create(..)
product.categories << category //assigning category to product
exmaple explain here HABTM

Related

How to list all the articles related to a certain category?

In my RoR app I have a problem with listing all items of a certain category. Here is 2 models:
class Article < ApplicationRecord
belongs_to :category
and
class Category < ApplicationRecord
has_many :articles
end
and my Category controller method :
def show
#articles = #category.articles
#category = Category.find(params[:id])
end
In db i have relation beetween categories and articles:
create_table "articles", force: :cascade do |t|
t.string "title"
t.text "text"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "category_id"
end
create_table "categories", force: :cascade do |t|
t.string "name"
t.text "desc"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
And in my article index view page have code where on link ("Category") name I want to show all articles of certain category
<td>
<% if article.category %>
<%= link_to article.category.name, category_path %>
<% end %>
</td>
But I receive error
No route matches {:action=>"show", :controller=>"categories"}, missing required keys: [:id]
You have to provide id or category object that responds to id method in order to generate the correct link:
<%= link_to article.category.name, category_path(article.category) %>

How to use a form to add to both joined tables in Rails

I have a joined table but looking for a way to input the information from a form into both tables, or have it work in general:
My schema:
create_table "categories", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "categories_listings", id: false, force: :cascade do |t|
t.integer "category_id", null: false
t.integer "listing_id", null: false
end
create_table "listings", force: :cascade do |t|
t.string "name"
t.text "description"
t.decimal "price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "image"
t.integer "user_id"
end
Models:
class Category < ApplicationRecord
has_and_belongs_to_many :listings
end
Listing < ApplicationRecord
belongs_to :category, required: false
belongs_to :categories_listings, required: false
end
Views
<%= form_with(model: listing, local: true) do |form| %>
...
<div class="space">
<%= form.select :category_ids, options_from_collection_for_select(Category.all, :id, :name), :prompt => "Select a Category", :multiple => true %>
</div>
...
Before i joined the tables, I had it working with a categories element (i believe thats the right term) within the listing tables that was attached to a categories table... You can see my previous post on SO where I was suggested to do this: Allowing multiple records in category to submit to listing
When i click submit, nothing enters into the categories_listings tables. Suggestions on how I make this happen?
The associations in your Listing model are wrong. It should be just
Listing < ApplicationRecord
has_and_belongs_to_many :categories
end
I suggest you to read has_and_belongs_to_many

Rails: show name instead of Id in a show page

I have two tables, accounts and items and I would like to show the buisness_name instead of the idfrom the accounts table on the view/items/show.html.erb page.
Currently I have no associations between the models, but I have the account_id column in the items table.
create_table "accounts", force: :cascade do |t|
t.string "buisness_name"
t.string "web_site"
t.string "phone_number"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "items", force: :cascade do |t|
t.string "title"
t.text "description"
t.string "image"
t.decimal "price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "category_id"
t.json "attachments"
t.integer "account_id"
end
I'm getting the account_id via this: <%= #item.account_id %>, but I would like to show the buisness_name instead.
Try something like this
class Item < ActiveRecord::Base
belongs_to :account
end
Into the view
<% if #item.account %>
<%= #item.account.buisness_name %>
<% end %>
Should be business_name, as #Sergio Tulentsev already told you
I updated my answer because I noticed from the table that the account_id has not a not null constraint
If you have
<%= #item.account_id %>
The horrible way to get the account would be
<%= Account.find_by(id: #item.account_id).try(:buisness_name) %>
Much smarter would be
class Item
belongs_to :account
delegate :buisness_name, to: :account, prefix: true, allow_nil: true
And then in the view...
<%= #item.account_buisness_name %>

Accessing nested forms data

I'm trying to build an app for online single page quizzes, so i'm using nested forms. My problem comes when i try to access data from nested classes, I'm confused on how to handle this as a best practice.
The app has a Quiz class, which has Questions, and each question has multiple Alternatives.
quizzes_controller.rb :
def take
#quiz = Quiz.find(params[:id])
#user_quiz = #quiz
#SAVE ANSWERS TO CURRENT USER
end
take.html.erb :
<%= simple_form_for #user_quiz do |quiz| %>
<%= quiz.simple_fields_for :questions do |question| %>
**<!-- this should show question.statement -->**
<div class="custom-controls-stacked">
<%= question.input :answer, collection: **#this should be question.alternatives**
,as: :radio_buttons, class:"custom-control-input" %>
</div>
<% end %>
<%= quiz.button :submit %>
<% end %>
quiz.rb :
class Quiz < ApplicationRecord
belongs_to :session
has_many :questions
accepts_nested_attributes_for :questions
end
question.rb
class Question < ApplicationRecord
belongs_to :quiz
has_many :alternatives
end
alternative.rb
class Alternative < ApplicationRecord
belongs_to :question
end
schema.rb
ActiveRecord::Schema.define(version: 20171008213618) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "alternatives", force: :cascade do |t|
t.text "answer"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "question_id"
t.index ["question_id"], name: "index_alternatives_on_question_id"
end
create_table "questions", force: :cascade do |t|
t.text "statement"
t.integer "correct_answer"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "quiz_id"
t.integer "answer", default: 0
t.index ["quiz_id"], name: "index_questions_on_quiz_id"
end
create_table "quizzes", force: :cascade do |t|
t.integer "minutes", default: 20
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "session_id"
t.index ["session_id"], name: "index_quizzes_on_session_id"
end
end
How can I access said data (question.statement is a string, and question has_many alternatives), when questions is a nested class?

Rails: How to sum quantities by category in the different models

I'd like to sum quantities by category in the different models (Shop and Item).
models
class Order < ActiveRecord::Base
has_many :shops
end
class Shop < ActiveRecord::Base
belongs_to :order
has_many :items
end
class Item < ActiveRecord::Base
belongs_to :shop
has_one :order, autosave: false, through: :shop
end
There are the columns category and quantity in the both shops and items table as below.
I'd like to sum and display both quantity by category.
schema.rb
ActiveRecord::Schema.define(version: 20160610051929) do
create_table "orders", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "shops", force: :cascade do |t|
t.string "name"
t.integer "order_id"
t.integer "category"
t.integer "quantity"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "items", force: :cascade do |t|
t.string "name"
t.integer "category"
t.integer "quantity"
t.integer "shop_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
orders_controller.rb
class OrdersController < ApplicationController
def show
#orders = Order.find(params[:id])
end
end
view/orders/show.html.erb
<%= render #orders %>
My current view is as follows;
Although it only calculate in items table, I'd like to add the quantity in shops.
view/orders/ _order.html.erb
<% order.shops.each do |shop| %>
<% shop.items.group(:category).sum(:quantity).each do |category, sum| %>
Category <%=category%> : <%= sum %><br>
<% end %>
<% end %>
Although I also tried the following view instead of the above code, it only works when the shop's category exist in item's category.
If there is no category same as shop's category in the item, the result is not what I'd like to.
view/orders/ _order.html.erb
<% order.shops.each do |shop| %>
<% shop.items.group(:category).sum(:quantity).each do |category, sum| %>
<% if shop.category.present? && shop.quantity.present? && category == shop.category %>
<% sum = sum + shop.quantity %>
<% end %>
Category <%=category%> : <%= sum %><br>
<% end %>
<% end %>
It would be appreciated if you could give me how to sum and display both quantities or better way.
SOLVED
I could do what I'd like to do as followings;
schema.rb
create_table "categories", force: :cascade do |t|
t.integer "category"
t.integer "quantity"
t.integer "shop_id"
t.integer "item_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "order_id"
end
create_table "items", force: :cascade do |t|
t.string "name"
t.integer "shop_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "orders", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "shops", force: :cascade do |t|
t.string "name"
t.integer "order_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
orders_controller.rb
class OrdersController < ApplicationController
def show
#orders = Order.find(params[:id])
#categories = Category.where(order_id: params[:id])
end
end
_order.html.erb
<% order.shops.each do |shop| %>
<% #categories.where("order_id = ?", order.id).each do |cate| %>
<%= cate.order_id %>, <%= cate.shop_id%>, <%= cate.item_id%>, <%= cate.category%>, <%= cate.quantity%> <br>
<% end %>
<% #categories.where("shop_id = ?", shop.id).group(:category).sum(:quantity).each do |category, sum|%>
Category <%=category%> : <%= sum %><br>
<% end %>
<% end %>
Your second approach should work, style-wise you can write it much shorter:
sum += shop.quantity.to_i if shop.category == category
nil.to_i is 0, so no matter if shop.quantity is nil or not you can safely add it as long as shop.category == category And if shop.categoryis nil, it won't be equal to category, so there is no need to ask for it's presence either.
That said, I still think you might want to rethink the way your models are.

Resources