I'm learning Rails and I'am pretty stuck building a nested form with many to many relationship.
I was able to get the many-to-many relationship working with has_many :through, but when it comes down to create the views and the controllers to get it working I'm getting stuck.
See models relationships below:
class Timesheet < ActiveRecord::Base
belongs_to :user
has_many :timesheet_payments
has_many :employees, :through => :timesheet_payments
accepts_nested_attributes_for :timesheet_payments,
:reject_if => :all_blank,
:allow_destroy => true
accepts_nested_attributes_for :employees
end
class Employee < ActiveRecord::Base
belongs_to :user
has_many :timesheet_payments
has_many :timesheets, :through => :timesheet_payments
end
class TimesheetPayment < ActiveRecord::Base
belongs_to :employee
belongs_to :timesheet
accepts_nested_attributes_for :employee,
:reject_if => :all_blank
end
See db schema below:
create_table "timesheet_payments", force: true do |t|
t.integer "employee_id"
t.integer "timesheet_id"
t.float "basic_hours"
t.float "sunday_bh_hours"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "timesheets", force: true do |t|
t.date "upload_date"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "employees", force: true do |t|
t.string "pps_no"
t.string "fname"
t.string "lname"
t.date "dob"
t.text "address"
t.string "ph_number"
t.float "basic_rop"
t.float "sunday_bh_rop"
t.string "email"
t.date "date_joined"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I want to create a form where I can create a new Timesheet, display all Employees and an field to add the basic_hours and sunday_bh_hours for each employee.
This would be similar to the idea of a relationship between Customer -> Order -> Products.
I hope this makes sense! Thanks in advance!
I tried this view form
<%= form_for(#payment, :html => {:multipart => true}) do |f| %>
<% if #payment.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#payment.errors.count, "error") %> prohibited this payment from being saved:</h2>
<% end %>
<!--creates a new timesheet -->
<%= f.fields_for :timesheet do |builder| %>
<%= builder.label "New timesheet" %>
<%= builder.text_field :upload_date %>
<p></p>
<% end %>
<!-- Add worked hours for each employee -->
<% #employee.each do |t| %>
<%= f.label t.fname %>
<br />
<%= f.label "Basic Hours" %>
<%= f.text_field :basic_hours %>
<%= f.label "Sunday/BH Hours" %>
<%= f.text_field :sunday_bh_hours %>
<br />
<% end %>
<%= f.submit 'Submit', :class => 'btn btn-primary' %>
<% end %>
It is loading fine but i really can't get my head around how I go about creating the "create" method on the controller.
I have the "new" method is following:
def new
#payment = TimesheetPayment.new
#timesheet = Timesheet.new
#employee = Employee.all
end
Related
I have a rails app that I'm trying to make an association with USERS (there are many) and Categories (there are 3, which I seeded). Each category has a name and an ID...
When I try to get users to edit their category, the form comes up blank.
The code I'm using for users to select their category is as follows :
<%= form_for (#user) do |f| %>
<%= f.select :categories, #user.categories.all, {multiple: true} %>
(i have also tried Category.all.collect)
Here is what my SCHEMA looks like
create_table "categories", force: :cascade do |t|
t.string "catname"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.index ["user_id"], name: "index_categories_on_user_id"
end
create_table "specialties", force: :cascade do |t|
t.string "specname"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "category_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.integer "category_id"
and here are my Models,
class User < ApplicationRecord
has_many :categories
class Category < ApplicationRecord
validates :catname, presence: true
has_many :specialties
belongs_to :user
You can try something like that.
<%= hidden_field_tag "user[category_ids][]", nil %>
<% Category.all.each do |category| %>
<%= check_box_tag "user[category_ids][]", category.id, #user.category_ids.include?(category.id), id: dom_id(category) %>
<%= label_tag dom_id(category), category.catname %> <br>
<% end %>
or
create a field categories in user, then:
<%= form.select :categories, Category.all.collect {|x| [x.name, x.id]}, {}, :multiple => true%>
I'm following this tutorial and this tutorial to learn more about has_many :through association in Rails. I created an app called school. And I have this inside my schema.rb file:
create_table "courses", force: :cascade do |t|
t.integer "teacher_id"
t.integer "student_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "quantity"
end
create_table "students", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "teachers", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
My teacher model:
class Teacher < ActiveRecord::Base
has_many :courses
has_many :students, :through => :courses
end
My student model:
class Student < ActiveRecord::Base
has_many :courses
has_many :teachers, :through => :courses
end
My course model:
class Course < ActiveRecord::Base
belongs_to :teacher
belongs_to :student
end
And my /courses view now looks something like this (I'm using scaffold):
When I go to /teachers/1, I want to display all students name and quantity associated with that teacher.
The current view of /teachers/1 is like this:
I try to make it by using this code but it's not working:
<% #course.each do |c| %>
<p><%= c.quantity %></p>
<% end %>
So, how to display all students name and quantity associated with that teacher?
<% #teacher.courses.each do |c| %>
<p><%= c.student.name %></p>
<p><%= c.quantity %></p>
<% end %>
You have to use the name of relation on variable with teacher object.
<% #teacher.courses.each do |c| %>
<%= c.quantity %>
<% end %>
<% #teacher.students.each do |s| %>
<%= s.name %>
<% end %>
Solved with this code:
<p id="notice"><%= notice %></p>
<p>
<strong>Name:</strong>
<%= #teacher.name %>
</p>
<table>
<tr>
<th>Student Name</th>
<th>Quantity</th>
</tr>
<% #teacher.courses.each do |c| %>
<tr>
<td><%= c.student.name %></td>
<td><%= c.quantity %></td>
</tr>
<% end %>
</table>
<%= link_to 'Edit', edit_teacher_path(#teacher) %> |
<%= link_to 'Back', teachers_path %>
Thanks to msergeant and Jan!
Made a previous post about this but after a lot of testing and changing it's outdated.
I'm trying to tie users and activities together.(One user can have several activities. An activity can have several users). I'ts pretty much a multi-user agenda thing.
Whenever i create a new activity i can select users by checkbox which will be doing the activity.
I can't get it to work though; The users won't show up in my activity show. Printing #activity.users in my show results in #<ActiveRecord::Associations::CollectionProxy::ActiveRecord_Associations_CollectionProxy_User:0x43d2158> but looping it, or checking my activities.js shows nothing(The activities.js shows "users":[] in the activity. So I'm pretty certain they're not associated properly.
Here's some code:
The activities.js index method
def index
#activities = Activity.all
respond_to do |format|
format.html
format.js {render_json #activities.to_json(:include => [:pictogram ,:users]) }
end
end
The activities' form(Loops all users)
<%= form_for(#activity) do |f| %>
<% if #activity.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#activity.errors.count, "error") %> prohibited this activity from being saved:</h2>
<ul>
<% #activity.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :start_date %><br>
<%= f.date_select :start_date %>
</div>
<div class="field">
<%= f.label :end_date %><br>
<%= f.date_select :end_date %>
</div>
<div class="users">
<% for user in User.all %>
<label class="activity">
<%= check_box_tag "activity[user_ids][]", user.id %>
<%= user.name %>
</label>
<% end %>
</div>
<div class="pictograms">
<% for p in Pictogram.all %>
<% #f.fields_for :pictograms do |x| %>
<%= p %>
<% #end %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The schema.rb
ActiveRecord::Schema.define(version: 20130911095113) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "activities", force: true do |t|
t.string "title"
t.date "start_date"
t.date "end_date"
t.integer "pictogram_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "activities", ["pictogram_id"], name: "index_activities_on_pictogram_id", using: :btree
create_table "activities_users", id: false, force: true do |t|
t.integer "activity_id"
t.integer "user_id"
end
add_index "activities_users", ["activity_id"], name: "index_activities_users_on_activity_id", using: :btree
add_index "activities_users", ["user_id"], name: "index_activities_users_on_user_id", using: :btree
create_table "pictograms", force: true do |t|
t.string "title"
t.string "url"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", force: true do |t|
t.string "name"
t.text "avatar"
t.date "birthdate"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Activity.rb
class Activity < ActiveRecord::Base
belongs_to :pictogram
has_and_belongs_to_many :users
accepts_nested_attributes_for :pictogram, allow_destroy: false
accepts_nested_attributes_for :users, allow_destroy: false
end
User.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :activities
validates_presence_of :name, :on => :create
validates_presence_of :birthdate, :on => :create
accepts_nested_attributes_for :activities, allow_destroy: false
end
And the activity params from my activities controller
def activity_params
params.require(:activity).permit(:title,
:start_date,
:end_date,
:users,
:pictogram)
end
Try sth like this:
class Activity < ActiveRecord::Base
belongs_to :pictogram
has_and_belongs_to_many :users
accepts_nested_attributes_for :pictogram, allow_destroy: false
def user_ids=(values)
self.users << User.find(values)
end
end
#controller
def activity_params
params.require(:activity).permit(:title,
:start_date,
:end_date,
{:user_ids =>[]},
:pictogram)
end
I suggest you try some minimalist debugging first,
User.create!(...) # create a valid user record
Activity.create!(...) # same as above
User.all.first.activities << Activity.all.first
User.all.first.activities.any?
See if this works, also keep an eye on your join_table and check that actual records are being persisted. Your schema looks good as well.
I think, none of them read about the strong_parameters with nested attributes :
You can easily define :users_attributes in permit parameters for nested_attributes.
activities controller :
def activity_params
params.require(:activity).permit(:title,
:start_date,
:end_date,
users_attributes: [:id])
end
This is the ideal way to do this. Thanks
Inside a fields_for block how can i reference the value of a relationship field.
For instance:
app/models/cart.rb
class Cart < ActiveRecord::Base
attr_accessible :lineitems_attributes
has_many :lineitems, dependent: :destroy
accepts_nested_attributes_for :lineitems
def total_price
lineitems.to_a.sum { |item| item.total_price }
end
end
app/models/lineitem.rb
class Lineitem < ActiveRecord::Base
attr_accessible :cart_id, :quantity, :package_id, :part_id
belongs_to :cart
belongs_to :package
belongs_to :part
def total_price
if package_id?
return package.price * quantity
end
if part_id?
return part.price * quantity
end
end
end
app/models/package.rb
class Package < ActiveRecord::Base
attr_accessible :description, :img_src, :name, :price
has_many :lineitems
end
app/views/cart/_form.html.erb
<%= form_for #cart do |f| %>
<%= c.fields_for :lineitems do |i| %>
<%= render 'lineitem_fields', :f => i %>
<% end %>
<%= c.submit %>
<% end %>
app/views/cart/_lineitem_fields.html.erb
<%= f.text_field :quantity %>
<% if :package_id? %>
<%= f.text_field :package_id %>
<% else %>
<%= f.text_field :part_id %>
<% end %>
<%= link_to 'Remove',
lineitem_path(:id),
:method => :delete,
:confirm => t('.confirm', :default => t("helpers.links.confirm",
:default => 'Are you sure?')) %>
relative pieces of schema
create_table "carts", :force => true do |t|
t.integer "branch_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "lineitems", :force => true do |t|
t.integer "cart_id"
t.integer "part_id"
t.integer "package_id"
t.integer "quantity", :default => 1
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "parts", :force => true do |t|
t.string "description"
t.string "partNumber"
t.decimal "price"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "packages", :force => true do |t|
t.string "description"
t.string "name"
t.string "img_src"
t.decimal "price"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
The above form works but...
Question 1: How to show the package.name instead of :package_id
Question 2: How to display total_price of each lineitem in the form. It is a method how would that work?
Question 3: Is there a best practice to displaying a form in an invoice looking manner where maybe the quantity is a text field but the remainder of the columns are just text or labels?
The end game scenario is this form will be a last chance to edit the quantities of the cart (or remove lineitems) before submitting an order. Obviously in the real world you want to display the quantities, package name, description and price but I can't seem to figure out how to display those values inside the form since they are in another model by relationship and not specific to lineitems.
Thanks for the help.
You are looking for the ActionView::Helpers::ActiveModelInstanceTag#object method. This gives you access to ActiveRecord everywhere inside the form.
1
<% if f.object.package_id? %>
<%= text_field_tag :package_name, f.object.package.name %>
<%= f.hidden_field :package_id %>
<% else %>
2 <%= f.object.total_price %>
3 Perhaps try the :readonly => true option on all input tags except the quantity?
The problem I am facing with Formtastic is that I have a Form to create a new Order. In this form I want to select multiple existing Food items from a list. These should be added to the new Order I am submitting. At the same time I also want to set attributes in the FoodOrder join model. This Model has an integer quantity attribute for which i would like to have a field in my form.
What I am basically looking for is a form that lists all Food Items and puts a field for the quantity on the same line as the Food Item it belongs to.
The Models
class Order < ActiveRecord::Base
belongs_to :user
belongs_to :restaurant
has_many :food_orders
has_many :foods, :through => :food_orders
end
class FoodOrder < ActiveRecord::Base
belongs_to :food
belongs_to :order
end
class Food < ActiveRecord::Base
has_many :food_orders
has_many :orders, :through => :food_orders
belongs_to :category
end
This is one of the versions of the form I have tried so far. But I am just baffled and do not know how to get fields for the FoodOrder Model.
<%= semantic_form_for [#restaurant, #order] do |f| %>
<%= f.inputs do %>
<%= f.input :comment %><br />
<%= f.input :table_id %><br />
<%# <%= f.input :foods, :as => :check_boxes %>
<%= f.inputs :for => :foods do |food| %>
<%= food %>
<%= food.inputs :quantity %>
<% end %>
<% end %>
<%= f.buttons do %>
<%= f.commit_button %>
<% end %>
<% end %>
My models have these attributes
create_table "food_orders", :force => true do |t|
t.integer "quantity", :null => false
t.decimal "price", :null => false
t.integer "food_id", :null => false
t.integer "order_id", :null => false
t.text "comment"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "foods", :force => true do |t|
t.integer "category_id", :null => false
t.string "name", :null => false
t.string "description"
t.string "image"
t.decimal "default_price", :null => false
t.boolean "active", :default => true, :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "orders", :force => true do |t|
t.integer "restaurant_id", :null => false
t.integer "user_id", :null => false
t.integer "table_id", :null => false
t.decimal "total", :null => false
t.datetime "finished_at"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
Basically what I would end up doing is creating a FoodOrder object for each Food then only save the ones that have a value above 0.
You could do that in a reject_if for the accepts_nested_attributes_for but that wouldn't deleted the old FoodOrders that someone might change from 3 to 0. So instead you will have to override the quantity= method for FoodOrder.
I've never used formtastic before but I'm going to take a guess and say that this should work.
<%= semantic_form_for [#restaurant, #order] do |f| %>
<%= f.inputs do %>
<%= f.input :comment %><br />
<%= f.input :table_id %><br />
<% #foods.each do |food|
<%= f.semantic_fields_for :food_orders,
#order.food_orders.detect_or_build_by_food(food)
do |food_order| %>
<%= food %>
<%= food_order.inputs :quantity %>
<% end %>
<% end %>
<% end %>
<%= f.buttons do %>
<%= f.commit_button %>
<% end %>
<% end %>
order.rb:
class Order < ActiveRecord::Base
belongs_to :user
belongs_to :restaurant
has_many :foods, :through => :food_orders
has_many :food_orders do
def detect_or_build_by_food(food)
record = self.detect{ |food_order| food_order.food_id == food.id }
if record.nil?
record = self.build(:food => food)
end
record
end
end
accepts_nested_attributes_for :food_orders
end
food_order.rb:
class FoodOrder < ActiveRecord::Base
belongs_to :food
belongs_to :order
def quantity=(quantity)
if quantity <= 0
self.destroy
else
self[:quantity] = quantity
end
end
end
I would hope that works but it is untested code, so yeah... Good Luck!