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?
Related
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
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 %>
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.
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
I am using Simple Form and the Invoicing gem, but when I try to render the form, I get "RuntimeError in InvoicingLedgerItems#new" with "Association :sender_id not found". I want to save the primary key of the Users table (Devise gem) in the sender_id field (because the user is the sender of the invoice). I tried using the foreign_key option in the model, but it didn't seem to have any effect. Here's my code without using the foreign_key option.
Models:
class InvoicingLedgerItem < ActiveRecord::Base
acts_as_ledger_item
belongs_to :user
has_many :line_items, class_name: 'InvoicingLineItem', foreign_key: :ledger_item_id
accepts_nested_attributes_for :line_items
end
class User < ActiveRecord::Base
has_many :invoicing_ledger_items
end
View:
<%= simple_form_for #invoicing_ledger_item do |f| %>
<%= f.association :sender_id %>
<%= f.button :submit %>
<% end %>
Schema:
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
t.datetime "deleted_at"
end
create_table "invoicing_ledger_items", force: true do |t|
t.integer "sender_id"
t.integer "recipient_id"
t.string "type"
t.datetime "issue_date"
t.string "currency", limit: 3, null: false
t.decimal "total_amount", precision: 20, scale: 4
t.decimal "tax_amount", precision: 20, scale: 4
t.string "status", limit: 20
t.string "identifier", limit: 50
t.string "description"
t.datetime "period_start"
t.datetime "period_end"
t.string "uuid", limit: 40
t.datetime "due_date"
t.datetime "created_at"
t.datetime "updated_at"
end
You Need to Make a relation with user model through foreign key 'sender_id'
Model
class InvoicingLedgerItem < ActiveRecord::Base
acts_as_ledger_item
belongs_to :user, foreign_key: :sender_id
has_many :line_items, class_name: 'InvoicingLineItem', foreign_key: :ledger_item_id
accepts_nested_attributes_for :line_items
end
Need to make association with user as hidden field
View:
<%= simple_form_for #invoicing_ledger_item do |f| %>
<%= f.association :user, :as => :hidden, :input_html => { :value => curren_user.id } %>
<%= f.button :submit %>
<% end %>
You should use user_id instead of sender_id in the schema first.Then use alias_attribute :user_id, :sender_id in InvoicingLedgerItem model, through this helper method you can assign a new name to the field name.
For more reference visit http://api.rubyonrails.org/classes/Module.html.