Ruby on Rails - how to belong to many? - ruby-on-rails

I have a table called Report, and another called Phases. Let's say I have 10 phases and I want to have checkboxes on the report form that lists out all the phases. How can I collect the items that are checked and store that data in the database?
Do I need to have columns such as phase_one:integer phase_two:integer phase_three:integer and just pull back the ones that aren't null? Or can I somehow store the IDs of x phases in one column and pull back those IDs in an array?

To clarify NateSHolland's answer, you can use has_and_belongs_to_many if the phases are predefined:
#app/models/report.rb
class Report < ActiveRecord::Base
has_and_belongs_to_many :phases
end
#app/models/phase.rb
class Phase < ActiveRecord::Base
has_and_belongs_to_many :reports
end
This will allow you to populate the phase_ids (collection_singular_ids) attribute from your form:
#config/routes.rb
resources :reports
#app/controllers/reports_controller.rb
class ReportsController < ApplicationController
def new
#report = Report.new
#phases = Phase.all
end
def create
#report = Report.new report_params
#report.save
end
private
def report_params
params.require(:report).permit(:phase_ids)
end
end
#app/views/reports/new.html.erb
<%= form_for #report do |f| %>
<%= f.collection_check_boxes :phase_ids, #phases, :id, :name %>
<%= f.submit %>
<% end %>
This would allow you to specify which phases your report has, although it will not have any changes.
What I think you'd be better doing is using has_many :through, allowing you to define which phase you're referencing:
#app/models/report.rb
class Report < ActiveRecord::Base
has_many :progress
has_many :phases, through: :progress
accepts_nested_attributes_for :progress #-> define level
end
#app/models/progress.rb
class Progress < ActiveRecord::Base
#id report_id phase_id phase_lvl created_at updated_at
belongs_to :report
belongs_to :phase
end
#app/models/phase.rb
class Phase < ActiveRecord::Base
has_many :progress
has_many :reports, through: :progress
end
The important factor here is the phase_lvl column - it's my understanding that you have certain "levels" which your phases will be a part of. I can't describe it properly, but to give you context, you'll have something like...
Report is written (phase 5)
Report is published (phase 3)
Report is Sent (phase 15)
The above will give you the ability to define which "level" each phase denotes. I think this is the distinction you're looking for.
This would be a little more tricky to implement, but worth it:
#app/controllers/reports_controller.rb
class ReportsController < ApplicationController
def new
#report = Report.new
#phases = Phase.all
10.times do
#report.progress.build
end
end
def create
#report = Report.new report_params
#report.save
end
private
def report_params
params.require(:report).permit(progress_attributes: [:phase_id, :phase_lvl])
end
end
This will give you the ability to define the following:
#app/views/reports/new.html.erb
<%= form_for #report do |f| %>
<%= f.fields_for :progress do |p| %>
<%= p.collection_select :phase_id, #phases, :id, :name %>
<%= p.number_field :phase_lvl %>
<% end %>
<%= f.submit %>
<% end %>

It sounds like what you may want is a many to many relationship or a has_and_belongs_to_many. To do this you will need to create a join table between Report and Process that will have a report_id column and a phase_id. For more information check out this documentation: http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
In the models you will need to add:
Class Report < ActiveRecord::Base
has_and_belongs_to_many :phases
...
end
and
Class Phase < ActiveRecord::Base
has_and_belongs_to_many :reports
...
end

Related

How to create multiple children objects after the parent has been created?

This is my scenario:
I have a an Order model and a Item model. They have the following relationship:
class Order < ApplicationRecord
has_many :items
end
class Item < ApplicationRecord
belongs_to :order
end
In my project, initially, I need to create the Order without Items. After that I need to create the items related to that order.
I have already tried user nested_attributes, however, I'm gonna need to created items more than once and in the second time try the Items I have already created shows up in the form for editing.
Any suggestions on the best approach?
EDIT:
Add one more info. I need the option to create multiple items at once.
An option could be to create first your Order, and then the items.
# config/routes
...
resources :orders do
resources :items
end
# app/controllers/itesm_controller.rb
class ItemsController < ApplicationController
def new
order = Order.find(params[:order_id]
#item = order.items.new
end
def create
item.create(item_params)
redirect_to orders_path # just guessing your paths
end
protected
def item_params
params.require(:item).permit(:your, :attributes, :here)
end
end
# Assuming Rails +5.1
# app/views/items/_form.html.erb
# you can use this partial in 'new.html.erb' and 'edit.html.erb'
<%= form_view model: #item do |form| %>
<%= form.label :your_attribute %>
<%= form.text_field :your_attribute %>
<%= form.submit %>

uninitialized constant ReviewsController::Reviews

I had this working very similar to another controller however i needed to change this relation to another controller called agreements_controller. I want to create a has one model. review has one and belongs to agreements.
Why isn't the row being created properly?
reviews_controller:
class ReviewsController < ApplicationController
def create
#review = Reviews.create(review_params)
end
private
def review_params
params.require(:review).permit(:comment, :star, :agreement_id, :user_id, :reviser_user_id)
end
end
_form.html.erb
<%= form_for([agreement, agreement.build_review] ) do |f| %>
<% end %>
agreement.rb
class Agreement < ActiveRecord::Base
has_one :review, :dependent => :destroy
end
review.rb
class Review < ActiveRecord::Base
belongs_to :agreement
belongs_to :reviser_user
belongs_to :user
end
I've tried to find similar examples online, but all I could find was nested forms... I don't need a nested form I just want the review to create as a has one.
Models are Singular. Use
Review.create(review_params)

Rails 4 Inventory Application: database design and use of nested forms

I built this application and it works nicely and is quite simple: https://github.com/ornerymoose/DeviceCount . It allows you to create a new entry for a device where you specify a count (ie, inventory amount) of a device.
Now even though this works, I've been told that it needs to be on a 'per location' basis. Ie, you create an entry and you will have 10 textfields (if there are indeed 10 devices. This amount will never change nor will the devices change) for devices, and for each device text field, you will enter a count for that device. You will choose location for a dropdown menu. When that entry is created, you will have:
-1 location
-10 Devices listed, all with their own count.
I'm struggling wrapping my head around how to design these models. Should I have an Entry and Device model? A separate Count model?
Would a nested form be the best approach here?
Any and all input is appreciated.
Sounds like you'd be best with an Inventory join model (with has_many :through):
#app/models/inventory.rb
class Inventory < ActiveRecord::Base
# id | device_id | location_id | qty | created_at | updated_at
belongs_to :device
belongs_to :location
end
#app/models/device.rb
class Device < ActiveRecord::Base
has_many :inventories
has_many :locations, through: :inventories
accepts_nested_attributes_for :inventories
end
#app/models/location.rb
class Location < ActiveRecord::Base
has_many :inventories
has_many :devices, through: :inventories
end
This will allow you to set the "quantity" of the device for each location (will have to use accepts_nested_attributes_for):
#app/controllers/devices_controller.rb
class DevicesController < ApplicationController
def new
#device = Device.new
#locations = Location.all
end
def create
#device = Device.new device_params
#device.save
end
private
def device_params
params.require(:device).permit(inventories_attributes: [:qty])
end
end
#app/views/devices/new.html.erb
<%= form_for #device do |f| %>
<%= f.text_field :name %>
<%= f.fields_for :inventories, Location.all do |i| %>
<%= i.number_field :qty %>
<% end %>
<%= f.submit %>
<% end %>
This will allow you to create a new Device and have it's qty available through its Inventory.

How to dispaly a login student written message in ruby on rails

I want to display login student name and message.
after login student can write messages and send related to courses.the messages he send is displayed above in same page with his/her name and message
I got name, but message field fetches all messages that are in database. How to display a particular student name and message?
Here is my code
controller.erb
class CourseQueriesController <ApplicationController
def index
#course_queries = CourseQuery.all
#course_query = CourseQuery.new
end
def create
#course_query = CourseQuery.new(student_id: current_student.id, coach_id: "2", message: params[:course_query][:message])
if #course_query.save
redirect_to course_queries_path, notice: 'Query was successfully send.'
else
render :new
end
end
end
course_queries/index.html.erb
<% #course_queries.each do |queries| %>
<p><b><%= current_student.name %></b></p>
<%= queries.message %>
<% end %>
<%= simple_form_for (#course_query) do |f| %>
<%= f.input :message %>
<%= f.button :submit , "Send or press enter"%>
<% end %>
how to display a particular student name and message
You need to have the relevant associations established in your models, like what Pavan wrote.
I'll give you some more information on why this is important...
ActiveRecord
One of the main reasons Rails works so well is the way it helps you create & manage objects. In OOP, objects form everything from your init commands to your user input responses, Ruby being a prime exponent of this structure.
Rails is built on Ruby, and therefore is object orientated too. It uses ActiveRecord, the MVC structure & classes to give you a platform from which you can populate and manipulate objects:
Thus, you shouldn't be treating your application's interactions as a way to edit a database, or "display a login message" - it should be a way to invoke & manipulate objects.
Objects - in the case of Rails - are built in the models. The model data can then be used in the controllers and views.
This seems to be lacking in your code. If you can remedy it, your code will become a lot simpler and more powerful...
Associations
I'd do something like this:
#app/models/student.rb
class Student < ActiveRecord::Base
has_many :queries
has_many :coarse_queries, through: :queries
end
#app/models/course.rb
class Course < ActiveRecord::Base
has_and_belongs_to_many :coaches
has_many :queries
has_many :student_queries, through: :queries
end
#app/models/coach.rb
class Coach < ActiveRecord::Base
has_and_belongs_to_many :courses
has_many :queries
end
#app/models/query.rb
class Message < ActiveRecord::Base
belongs_to :course
belongs_to :student
belongs_to :coach (maybe)
end
This structure will allow a student to send queries to specific courses, selecting the coach as necessary. Importantly, this sets up your associations so that you don't have to invoke multiple classes each time you want to populate the various objects.
#app/controllers/course_queries_controller.rb
class CourseQueriesController <ApplicationController
def index
#queries = Query.all
#query = current_student.queries.new
end
def create
#query = current_student.queries.new query_params
if #query.save
redirect_to course_queries_path, notice: 'Query was successfully send.'
else
render :new
end
end
private
def query_params
params.require(:query).permit(:message).merge(coach_id: "2")
end
end
#app/views/queries/index.html.erb
<% #queries.each do |query| %>
<p><b><%= query.student.name %></b></p>
<%= query.message %>
<% end %>
<%= simple_form_for #query do |f| %>
<%= f.input :message %>
<%= f.button :submit , "Send or press enter"%>
<% end %>
You should add has_many :course_queries to the Student model
#student.rb
class Student < ActiveRecord::Base
has_many :course_queries
...
end
And in the controller in index method change #course_queries = CourseQuery.all to #course_queries = current_student.course_queries
Now <%= queries.message %> will only display the course_query's message of the current_student

Rails: Sum in 3rd level of nested resource

in my rails 3.1 application I have these models:
class Project < ActiveRecord::Base
has_many :tasks
has_many :hours, :through => :tasks
def sum_hours
self.hours.sum(:hours)
end
end
class Task < ActiveRecord::Base
belongs_to :project
has_many :hours
def sum_hours
self.hours.sum(:hours)
end
end
class Hour < ActiveRecord::Base
attr_accessible :hours # column in table
belongs_to :task
end
in projects_controller.rb this:
def show
#project = Project.find(params[:id])
#tasks = #project.tasks.includes(:hours)
end
and in projects/show.html.erb I use this:
...
<td>Sum hours: <%= #project.sum_hours %></td>
...
<% for task in #tasks do %>
<tr>
<td><%= task.sum_hours %></td>
</tr>
...
But even with .includes(:hours) it still uses a query separately for the project and for every task. Am I missing something?? where am I doing mistake???
You can try something like this,
#tasks = #project.tasks.joins(:hours).select("tasks.*, sum(hours) as total")
Then you can access the sum by,
task["total"].to_i
I cannot ensure these code works for you. But the basic idea is to join the table and select the sum as an extra field.

Resources