Question about controllers, dependent models, and controller functions - ruby-on-rails

I'm new to ruby on rails and I'm thoroughly confused from all the documentation online. I'm creating a database program that keeps track of orders. I've already setup the has_one, belongs_to, and has_many associations in the model and I can verify their relationship with my sqlbrowswer.
Here's an rough illustration of the models.
Models
Each order has only one product, one service, one bill, and each bill can have many charges. The same product may appear in different orders, but each order would be unique.
I also have an order_observer model to create dependent models of the product
class OrderObserver < ActiveRecord::Observer
observe :order
def after_create(order)
Product.create! :order_id => order.id
Service.create! :order_id => order.id
Bill.create! :order_id => order.id
end
end
Here's what my product class and order class look like:
class Product < ApplicationRecord
belongs_to :order
end
class Order < ApplicationRecord
has_one :product, dependent: :destroy
has_one :bill, dependent: :destroy
has_one :service, dependent: :destroy
accepts_nested_attributes_for :product
accepts_nested_attributes_for :bill
accepts_nested_attributes_for :service
end
Now, what I'm confused on is how to update the dependent models. Should I implement multiple controllers for each model or just one controller to handle the whole thing?
Right now all I have in my main controller is:
def create
#order = Order.new(order_params)
end
def edit
#order = Order.find(params[:id])
end
def update
end
I would appreciate any tips or guidance.
Thanks!

Now, what I'm confused on is how to update the dependent models. Should I implement multiple controllers for each model or just one controller to handle the whole thing?
You can do this whatever you want, but you should always remember the idea 'resources', In this case, each model represents a resource, so you should do it separately.
The Rails program is based on resources, as you can see that in 'routes.rb'.

Related

How to get sum of invoices across join table in Ruby on Rails?

I have these models in my Rails 4 app:
class Invoice < ActiveRecord::Base
has_many :allocations
has_many :payments, :through => :allocations
end
class Allocation < ActiveRecord::Base
belongs_to :invoice
belongs_to :payment
end
class Payment < ActiveRecord::Base
has_many :allocations
has_many :invoices, :through => :allocations
end
Obviously, it is possible that one payment belongs to many invoices.
In the Payment model I have this function that sums the total of all the invoices that one specific payment relates to:
def invoice_total_of_siblings
invoice_ids = Allocation.where(:payment_id => id).map(&:invoice_id)
invoices = Invoice.where(:id => invoice_ids)
invoices.to_a.sum(&:total)
end
However, this function feels cumbersome and I wonder how it could be made more concise.
Thanks for any help.
Using your set of associations (payment has many invoices through allocations), you could simply do this:
def invoice_total_of_siblings
invoices.sum(:total)
end
EDIT:
This solution works as-is for database fields, provided that the given set is an ActiveRecord association.
In this speicific case, however, as it is produced from the comments, total is a calculated field. Hence, the given set will not be an ActiveRecord association, but an Array. Then, you will need to map the field in order to sum it. The correct syntax in this case will be:
def invoice_total_of_siblings
invoices.sum(&:total)
end

rails model assignment with has_many :through

I just can't figure out how to create a relation with a join table. I've read all the posts about them, but the main error seems to be that in the join table to models should be singular, which I have. I just can seem to create the models correctly and assign them. I have projects with datasets, and projects can have multiple datasets, while a dataset can belong to multiple projects. A dataset can be active or not, which is why I need the has_many through instead of the has_many_and_belongs_to setup.
My model definitions are:
class Project < ActiveRecord::Base
attr_accessible :name, :user_id
belongs_to :user
has_many :activedatasets
has_many :datasets, :through => :activedatasets
end
class DataSet < ActiveRecord::Base
attr_accessible :name, :project_id, :filename, :tempfilename
has_many :activedatasets
has_many :projects, :through => :activedatasets
end
class ActiveDataSet < ActiveRecord::Base
attr_accessible :active, :data_set_id, :project_id
belongs_to :project
belongs_to :dataset
end
When I create a new dataset I've got the project_id in the params, so I'm trying to setup the relationship like below:
class DataSetsController < ApplicationController
def new
#dataset = DataSet.new
#dataset.activedatasets.project_id = params[:project_id]
end
end
The error I'm getting seems famous:
NameError in DataSetsController#new
uninitialized constant DataSet::Activedataset
Can anybody point me in the right direction please?
Thanks for you attention.
You need to use:
has_many :active_data_sets
has_many :data_sets, :through => :active_data_sets
And in the DataSet model:
has_many :active_data_sets
has_many :projects, :through => :active_data_sets
Basically, rails expects you to use underscores to separate words in association names, and converts them to CamelCase. So active_data_sets becomes ActiveDataSet. Rails then uses this to work out which model class the association is with.
You also need to change your controller to this:
class DataSetsController < ApplicationController
def new
#dataset = DataSet.new
#dataset.active_data_sets.build(:project_id => params[:project_id])
end
end
Otherwise you'll get an error because you tried to set the project_id of the active_data_sets collection rather than creating a new ActiveDataSet.

Create and destroy HABTM associations - not actual records?

This is similar to a previous question, but our approach has changed a little since.
I need to be able to destroy the associations between Releases & Tracks + Products & Tracks without destroying the track itself. I need to then be able to re-associate a Track with any Release or Product.
My models are as follows:
class Release < ActiveRecord::Base
has_many :products, :dependent => :destroy
has_and_belongs_to_many :tracks
end
class Product < ActiveRecord::Base
belongs_to :release
has_many :releases_tracks, :through => :release, :source => :tracks
has_and_belongs_to_many :tracks
before_save do
self.track_ids = self.releases_track_ids
end
end
class Track < ActiveRecord::Base
has_and_belongs_to_many :releases
has_and_belongs_to_many :products
end
class ReleaseTracks < ActiveRecord::Base
belongs_to :release
belongs_to :track
end
class ProductsTracks < ActiveRecord::Base
belongs_to :product
belongs_to :track
end
Can anyone help with the appropriate controller methods please?
If I use the following on a Product Track for example, it destroys the track and association:
#product = Product.find(params[:product_id])
#track = #product.tracks.find(params[:id])
#track.destroy
I've tried something like this but it gives me a notmethoderror:
#product = Product.find(params[:product_id])
#track = #product.products_tracks.find(params[:track_id])
#track.destroy
Can anyone point me in the right direction? As I say, I need this primarily on destroy, but i also need to then be able to re-create associations.
has_and_belongs_to_many (HABTM) is one way of declaring a many-to-many relationship between two models. Under the covers, rails uses a simple join table (a table with two columns only, each a foreign key to the primary key of the main tables). The association exists when a record in the join table exists. I am not sure how destroy works by default in this case, but it may decide to delete all of the associated records (not just the association). You may be able to control this by using the dependent <action> clause on the HABTM declaration. The relationship is created when you assign one or more "children" to a parent and save.
In this case, maybe what you want is a pair of has_many :through relations -- the "through" is a relation, but can be treated as a first class model, and extended and operated on with ActiveRecord, meaning you can get at a specific release track (for example) and decide to delete it without affecting either association.
There's a good section on when to use one vs. the other in this Rails Guide: http://guides.rubyonrails.org/association_basics.html#choosing-between-has_many-through-and-has_and_belongs_to_many

RoR: Creating a relationship between a purchased product and a user

Hello and thanks for any help in advance.
I want to create a relationship between a user and a product(pdf) upon an order being saved.
class PDF
has_many :line_items
end
class LineItem
belongs_to :pdf
belongs_to :cart
end
class Cart
has_many :line_items
has_one :order
end
class Order
belongs_to :cart
end
Upon the user purchasing a line_item, I would like to create relationships through a join model between user and pdf (pdf_relationships).
I'm trying to find each PDF(found by foreign_key line_item.pdf_id) in a given cart and create pdf_relationships between a user and each pdf in the cart. I'll be making the user's id the owner id and making the pdf's id the owned_id.
My order controller looks like this:
def create
#order = current_cart.build_order(params[:order])
#order.ip_address = request.remote_ip
if #order.save
if #order.purchase
render :action => "success"
else
render :action => "failure"
end
else
render :action => 'new'
end
end
and what I'm having trouble with is this:
class Order
belongs_to :cart
before_save :create_owner
***def create_owner
self.cart.line_items.each do |item|
pdf.find_by_item_pdf_id(:pdf_id)
current_user.pdf_relationships.build(:owned_id => pdf.id)
end
end***
end
here is my user model:
class User
has_many :line_items
has_many :pdf_relationships, foreign_key: :owner_id, :dependent => :destroy
has_many :pdfs, foreign_key: :user_id, dependent: :destroy
has_many :pdf_ownings, :through => :pdf_relationships, :source => :owned
def owning?(owned)
pdf_relationships.find_by_pdf_owned_id(owned)
end
def own!(owned)
pdf_relationships.create!(:owned_id => owned.id)
end
def unown!(owned)
pdf_relationships.find_by_pdf_owned_id(owned).destroy
end
I hope this is clear enough. I've been trying to figure this out for sometime now and definitely trying to get past being just a novice. Suggestive readings are definitely welcome too!
It looks like you have the right idea.
Since LineItem has many PDFs, you can just use the pdf_id from the LineItem without bothering to fetch the record from the database. You can also append those IDs to the existing set of user-PDF associations as if you were just pushing items onto an array.
However, your model won't have access to the current session (since that's handled by the controller), so you'll have to pass the current user to the Order some other way, perhaps as part of the purchase method.
class Order
belongs_to :cart
def purchase(user)
# ... existing logic ...
user.pdf_relationship_ids << cart.line_items.map(&:pdf_id)
end
end
You will also have to declare the association between User and PDF and create the migrations, but it sounds like you're already planning to do that.
Update: I think you can greatly simplify your User model by taking advantage of has_many :through. Here's what I envision:
class User < ActiveRecord::Base
has_many :orders
has_many :line_items, :through => :orders
has_many :pdfs, :through => :line_items
end
class Order < ActiveRecord::Base
belongs_to :user
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :order
has_one :pdf
end
class Pdf < ActiveRecord::Base
belongs_to :line_item
end
Then you don't have to explicitly specify that a user owns a PDF. It's implicit in the user -> order -> line item -> PDF relationship.
This may not solve your problem as-is. You wouldn't be able to "disown" a PDF without deleting the original line item, which is probably not what you want. But I think you should try to aim for something like this. Take advantage of Rails's built-in associations as much as you can.

Instantiating a model with association

I have a question regarding instantiating a model with a belongs_to association.
Taken from the start of http://guides.rubyonrails.org/association_basics.html :
class Customer < ActiveRecord::Base
has_many :orders, :dependent => :destroy
end
class Order < ActiveRecord::Base
belongs_to :customer
end
This instantiation works:
#order = #customer.orders.create(:order_date => Time.now)
But would this work just as well?
class Order < ActiveRecord::Base
attr_accessible :customer
belongs_to :customer
end
#customer = Customer.new
#order = Order.create(:customer => #customer)
My experiments indicate that it does, to some extent.. But since associations are loaded lazily, it might be tricky in some cases (I can give one example, if you'd like).
So my question is:
To what extent does that instantiation work just as well as the former?
These two forms both work.
Either way you have an Order object with a customer_id field set to the ID of an existing customer. When you call customer.orders.create() it's populating that association behind the scenes. In your second example you are doing it manually.

Resources