How to create multiple children objects after the parent has been created? - ruby-on-rails

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 %>

Related

Class Variables instantiated(refreshed) for each new instance

I have two classes Category and order_item, where an order_item belongs_to a category.
I want to add unique 'category' items to a class variable that is an array of 'categories' and is represented by an array class variable ##categoriesList, each time a new category is detected among the instance variables.
This is what i tried.
class OrderItemsController < ApplicationController
##categoriesList = []
def create
#order_item = OrderItem.new(order_item_params)
if #order_item.save
#total = #order_item.order.total
#orderItemCategory = #order_item.category
if #orderItemCategory.set?
if !(##categoriesList.include? #orderItemCategory)
#total += #orderItemCategory.price
##categoriesList.push(#orderItemCategory)
end
........
........
end
Code Explanation:
I don't want the price of the next instance of order_item to be taken in account, if the price of a previous order_item that belongs to a same category's price has already been taken in account for.
For example: Egg and Milk both belong to Combo-1. So i just want to take into account the price of Combo-1 once, and not for each order_item instance, i.e. egg and milk, which would double the total amount.
What I tried:
I push the category name of the order_item after it's price has been taken in account. And when the next order_item is created, i checked if that order_item's category's price has already been recorded by checking it in the current ##categoriesList class variable.
Problem:
At each instance when i check the class variable ##categoriesList, it returns an empty array list, and no previous records that have been pushed to that array is shown.
I want something like a static variable in java, where every instance of the class shares the same variable, without actually refreshing the data in the variable for each instance.
You don't want a class variable pretty much ever since they are not thread safe or actually persistent across requests which really applies to almost any programming language. Every time the code is reloaded the class variable will be reset.
What your trying to do can really be done by modeling the domain properly. In the case of a checkout system its been done a billion times already and the common pattern is:
class Order < ApplicationRecord
has_many :line_items
has_many :products, through: :line_items
end
# rails g model line_item quantity:decimal unit_price:decimal order:belongs_to product:belongs_to
class LineItem < ApplicationRecord
belongs_to :order
belongs_to :product
end
class Product < ApplicationRecord
has_many :line_items
has_many :orders, through: :line_items
end
On the line items table which represents an actual line on an order form you store what order the item belongs to, quantity & the unit price at the time of purchase. To tally an order you sum the line items:
class LineItem < ApplicationRecord
# ...
def net
quantity * unit_price
end
end
class Order < ApplicationRecord
# ...
def net
line_items.sum(&:net)
end
end
So then your can just call order.net and it will give you the net sum. I have no clue where you're going with that categories mess but if we looked up the price here by going to the product we would not be able to account for past transactions unless the prices are completely static.
This is how you would handle creating line items:
resources :orders do
resources :line_items
end
class LineItemsController < ApplicationController
before_action :set_order
# GET /orders/1/line_items
def new
#line_item = #order.line_items.new
#products = Product.all
end
# POST /orders/1/line_items
def create
#line_item = #order.line_items.new(line_item_params) do |items|
items.unit_price = li.product.price # stores the price at time of purchase
end
if #line_item.save
redirect_to #order
else
#products = Product.all
render :new
end
end
# ...
private
def line_item_params
params.require(:line_item)
.permit(:quantity, :product_id)
end
def set_order
#order = Order.find(params[:order_id])
end
end
<%= form_with(model: [#order, #line_item], local: true) do |f| %>
<div class="field">
<%= f.label :product_id %>
<%= f.collection_select :product_id, #products, :id, :name %>
</div>
<div class="field">
<%= f.label :quantity %>
<%= f.number_field :quantity %>
</div>
<%= f.submit %>
<% end %>

Rails 4 Tagging users on a model

Whats the best method on tagging users? If you have a team model, and when you create a team, you want to add the members, how would this architecture work?
I was thinking of just using acts as tanggble and use it on the users, but not sure if this would be the best method? Is there another gem out there that would do something like this?
It sounds like you're looking for a has many through relationship. This would require you to have a joining table called team_members to record which users are members of each team, having user_id and team_id columns. So for example your Team model would have a relationship that looks like this:
has_many :users, through: :team_members
This then defines the appropriate method on Team for adding, querying and removing users.
More information is here
To add to #tpbowden's answer, if you just want to "tag" users, you may wish to use has_and_belongs_to_many:
# app/models/user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :teams
end
# join table "teams_users" - team_id | user_id
# app/models/team.rb
class Team < ActiveRecord::Base
has_and_belongs_to_many :users
end
This will allow you to use the singular_collection_ids method, with which you'll be able to define which user is in a "team":
#app/controllers/teams_controller.rb
class TeamsController < ApplicationController
def edit
#team = Team.find params[:id]
end
def update
#team = Team.find params[:id]
#team.update team_params
end
private
def team_params
params.require(:team).permit(user_ids: [])
end
end
#app/views/teams/edit.html.erb
<%= form_for #team do |f| %>
<%= f.collection_select :user_ids, User.all, :id, :name %>
<%= f.submit %>
<% end %>
This is as close to "tagging" as you're going to get without any extra dependencies.

Ruby on Rails - how to belong to many?

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

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

Build form inside show action for outside model

I am trying to build a form inside the show action of a controller. Here is my setup.
Category Model
class Category < ActiveRecord::Base
# Associations
has_many :feeds
has_many :subscriptions
# Nested attributes
accepts_nested_attributes_for :subscriptions
end
Subscription Model
class Subscription < ActiveRecord::Base
# Associations
belongs_to :profile
belongs_to :category
belongs_to :feed
end
Using a Subscription namespace:
# Subscriptions
namespace :subscriptions do
resources :categories
end
Inside the show action for a category (http://URL.com/subscriptions/categories/1), I want to build the subscriptions model form and list the available feeds for that category. I want the form to submit the checked off feeds to the Subscription model.
CategoriesController
def show
#feeds = Feed.where("category_id = ?", #category)
end
Trying to build the form in the Show action of the Categories view:
<%= simple_form_for [:subscriptions, #category] do |f| %>
<%= f.association :feeds, collection: #feeds, value_method: :id, as: :check_boxes %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Right now it is rendering the appropriate information, but it is rendering as an edit form, not a create form. How do I fix this?
What else do I need to add to my routes / controller to actually get the information to save to the Subscription model?
Thanks.
first thing i am not sure where you initialize the #category object in show action and in view. if you want to render a new page than it should be map to new action of the controller and you path with will be
http://URL.com/subscriptions/categories/new
the path you are using
http://URL.com/subscriptions/categories/1
will always map to the show action according to rails convention though you can override that for you accordingly but that is not a good practice. also in new action initialize the instance by
#category = Category.new
now on submit it will automatically goes to the create action. if your #category is already persisted in the database for example
#category = Category.find(1)
now if this object goes to the simple_form_for it will send the data to update action .

Resources