Rails: Relationship between two loosely related models - ruby-on-rails

I am working on a Ruby on Rails 3 web application and am not sure how to relate two of the models.
In our organization sales reps go out on appointments. If the appointment is successful, it will result in creating an order (which then has the items ordered related to it, but that's for another day.) If this appointment is not successful, it will be marked as no sale and as you might have guessed, no order is created.
On the other hand, sometimes sales happen without an appointment. For example, a customer may call into the store and order something. In this case, an order can exist without an appointment.
It would be simple if there were no relationship between orders and appointments, but there has to be for ease of use for the end user. For example, if an appointment generates an order, but later the buyer cancels, they will mark the appointment as sale cancelled and then the system should automatically set the order as cancelled. Likewise,they may choose to cancel the order, then the appointment would have to be cancelled automatically by the system.
How does a developer handle something like this? Does the appointment :have_many => orders? does the order :belong_to => appointments? I don't know what to do!
Please help me with this, I am a pretty new rails developer and I feel in over my head! Thank you!

As you already said, the following will work fine:
class Appointment < ActiveRecord::Base
has_many :orders
end
class Order < ActiveRecord::Base
belongs_to :appointment
end
belongs_to requires the field appointment_id to be present in the orders table. But, if the order is not associated with an order then appointment_id does not need to be set. You can have multiple belongs_to associations for a given class.

Related

Looking for a way to track history in rails database

I'm considering this an add-on question of sorts to the thread below:
Using join tables in ruby on rails
So we have 'Student' and 'Course' scaffolds joined by a many-to-many association, but in addition there is also a 'Semester' scaffold and what I wish to do is, for each student that is added to a course, for the application to search for previous iterations of the same course through past semesters, to that it's known how many times a student has taken that class before. I'm kind of mixed up at the moment as to how to implement this, so I was hoping someone could help me pin down the logic and code I should be operating by.
Some underlying assumptions I have so far:
'Course' and 'Semester' should, like 'Student' and 'Course', be joined
by a many-to-many association (many courses are taught per semester,
and a course is taught for more than one semester).
There should be an action (let's say get_student) within the course
controller to locate the student via student_id. This would be the main area I'm scratching my head as to what to do. What would this method look like in code?
Within the student-course join table I should have an attribute
'attempts' which increments each time get_student finds this
student_id combined with the course_id that calls the method.This
would be the mechanism that actually tells how many times the course
had been attempted by the student.
I initially wondered if there should be a 'semester' controller
action to activate get_student across all semesters, but now I'm
thinking that get_student should work fine without that.
Appreciate any help I can get on this. Thanks.
This is not a good answer, just a comment.
I would comment, but hear will be more clear. I ll update for the other points. This is just an ongoing feedback/discussion, not an answer.
class Semester < ApplicationRecord
has_many :courses
end
class Course < ApplicationRecord
has_many :students
end
And
semester.courses[0].students => outputs the students array for that
This could be the method to calculate the number of student that did that course:
def studentForCourse
#input_params.course_id => course id you are selecting
semester = Semester.find(input_params)
semester.courses.each do |course|
if course.id = input_params.course_id
nstudents = course.students.size
end
end

Rails 4 - Saving/deleting with one-to-one association

I'm building an application where I have one-to-one relationship, Reservation 'has_one' Order. Now my question is, how can I delete Reservation from the DB if a user decides to not completing the form for Order? I've tried looking up ways to do this but biggest issue is not knowing what to search for.
Currently my create action saves the Reservation once the form is submitted and then redirects to the form for Order payment to be completed.
Instead of worrying about deleting a Reservation if an order isn't complete, why not just prevent creation of the Reservation in the first place?
class Reservation
has_one :order
accepts_nested_attributes_for :order
validates_presence_of :order
Consider combining the creation of both objects into a single page, or passing params or objects between steps of your controller.
You should create both of them at one time
I think your best option is to use before_save in Order model to create a Reservation
Also check for accepts_neseted_attributes so both of them can be created with each other

Rails: modelling balance movement

Hello I have 2 models here:
class Account < ActiveRecord::Base
belongs_to :user
has_many :transactions
end
class Transaction < ActiveRecord::Base
belongs_to :account
after_create :update_balance
def update_balance
balance = self.account.balance
self.account.update(balance: balance - self.price)
end
end
1) Currently, I have an account with balance, and able to update the balance everytime I have created a transaction. But in the future, I will have a updated transaction, where it could get a new price, which I store in another field call new_price, and once we update the new price, how can we add to the account balance?
2) We are going to chart an account balance movement, so we need to store every account balance history, is it a good way if we create a new account field, for everytime we got a new transaction?
Thanks.
There should be a bit more complex logic. About your questions:
1) You shouldn't update payment transaction because you would get a side affects (like not valid balance on some accounts, transaction history, etc.). The better way is a rollback your transaction. There you would pass validation logic (e.g. cannot rollback if future balance of asset account will be negative, etc.)
2) For storing payment history you should use your Transaction model.
Also there a lot of another interesting moments. If you want you can see my gem for this job. You can install it (if you use Rails) or use some business logic for your payment system.

Review model - shared across two different other models

I am making an application that lets people list food items that they want to sell. They list Dishes (Dish model) and when people buy it, an Order record is created.
I want people to be able to submit reviews on Dishes if they have purchased it, and also review the Order (the order involves meeting the person).
I was wondering if I could create an Review model as follows:
class Review < ActiveRecord::Base
belongs_to :dish
belongs_to :order
end
I feel like this isn't right. The reviews on Dishes and the reviews on Orders will have different fields and potentially a completely different kind of logic, but at the same point in time its the same "idea" so I thought it would be better to create only one model for it.
Is the above correct?
Thanks
This is the use case that single table inheritance (STI) was designed for. When most of the values are shared between two models, STI can let you inherit both of them from another model. E.g.:
class Review < ActiveRecord::Base; end
class DishReview < Review
belongs_to :dish
end
class OrderReview < Review
belongs_to :order
end
You then store the review type in a new type column, and ActiveRecord handles storing them both in the same reviews table. The reviews table will need foreign keys for both a dish and an order.
If these review types may diverge much ... say you want to add spicyness and sweetness, etc. to dish reviews, and speed and friendliness, etc., to the order reviews ... then it makes more sense to start with separate tables, one for DishReview and another for OrderReview. You can then use modules, services, concerns, etc., to share logic between them. You can also store common data in the Review table and have DishReview and OrderReview link one-to-one with that.

How to actually use has_many :through and set up relationships

So many tutorials on how to set up a has_many :through but not enough on how to actually do it!
I have a Inventories and Requests table joined by Bookings. Example: there could be 3 lenders who have tents in inventory, each of which is requested by 3 other borrowers. What I want to do is for each of the 3 tents in inventory, show that lender the list of 3 borrowers who requested the tent. Then the lender can pick who s/he wants to be the ultimate borrower.
I have thoughts on how this should work, but no idea if it's right, so please give advice on the below! The action is driven all by the Requests controller. Let's run through an example where the Inventories table already has 3 tents, ids [1, 2, 3]. Let's say Borrower Pat submits a Request_ID 1 for a tent.
Am I then supposed to create 3 new Bookings all with Request_ID 1 and then Inventory_ID [1, 2, 3] to get all the conceivable combinations? Something like
Inventory.where(name: "tent").each { |inventory| #request.bookings.create(inventory_id: inventory.id) }
And then is it right to use the Bookings primary key as the foreign key in both the Request and Inventory? Which means that after Borrower Pat submits his request, the bookings_id will be blank until say Lender 2 accepts, at which point bookings_id equals the id that matches the combination of Request_ID 1 and Inventory_ID 2
Now let's say when a Request is posted and a Bookings is made, I email the lender. However, I realized I don't want to bother Lender Taylor if 3 borrowers want her tent over the same time period. I'll just email her the first time, and then the subsequent ones she'll find out about when she logs in to say yes or no. In this situation is it OK to just query the Bookings table in the create action, something like (expanding off above)
-
Inventory.where(name: "tent").each do |inventory|
if !Bookings.find_by_inventory_id(inventory.id).exists?
# If there are no other bookings for this inventory, then create the booking and send an email
#request.bookings.create(inventory_id: inventory.id)
AlertMail.mail_to_lender(inventory).deliver
else
# If there are other bookings for this inventory, do any of those bookings have a request ID where the requested time overlaps with this new request's requested time? If so then just create a booking, don't bother with another email
if Bookings.where(inventory_id: inventory.id).select { |bookings_id| Bookings.find_by_id(bookings_id).request.time overlaps_with current_request.time }.count > 0
#request.bookings.create(inventory_id: inventory.id)
# If there are other bookings for this inventory but without overlapping times, go ahead and send an new email
else
#request.bookings.create(inventory_id: inventory.id)
AlertMail.mail_to_lender(inventory).deliver
end
end
end
Code above is probably flawed, I just want to know the theory of how this is supposed to be working.
Join Table
Firstly, has_many :through works by using a join table - a central table used to identify two different foreign_keys for your other tables. This is what provides the through functionality:
Some trivia for you:
has_and_belongs_to_many tables are called [plural_model_1]_[plural_model_2] and the models need to be in alphabetical order (entries_users)
has_many :through join tables can be called anything, but are typically called [alphabetical_model_1_singular]_[alphabetical_model_2_plural]
--
Models
The has_many :through models are generally constructed as such:
#app/models/inventory.rb
Class Inventory < ActiveRecord::Base
has_many :bookings
has_many :requests, through: :bookings
end
#app/models/booking.rb
Class Booking < ActiveRecord::Base
belongs_to :inventory
belongs_to :request
end
#app/models/request.rb
Class Request < ActiveRecord::Base
has_many :bookings
has_many :requests, through: :bookings
end
--
Code
Your code is really quite bloated - you'll be much better doing something like this:
#app/controllers/inventories_controller.rb
Class InventoriesController < ApplicationController
def action
#tents = Inventory.where name: "tent"
#tents.each do |tent|
booking = Booking.find_or_create_by inventory_id: tend.id
AlertMail.mail_to_lender(tent).deliver if booking.is_past_due?
end
end
end
#app/models/booking.rb
Class Booking < ActiveRecord::Base
def is_past_due?
...logic here for instance method
end
end
Used find_or_create_by
You should only be referencing things once - it's called DRY (don't repeat yourself)
I did a poor job of asking this question. What I wanted to know was how to create the actual associations once everything is set up in the DB and Model files.
If you want to create a record of B that is in a many-to-many relationship with an existing record of A, it's the same syntax of A.Bs.create. What was more important for me, was how to link an A and B that already existed, in which case the answer was A.B_ids += B_id.
Two other things:
More obvious: if you created/ linked something one way, was the other way automatic? And yes, of course. In a many-to-many relationship, if you've done say A.B_ids += B_id, you no longer have to do 'B.A_ids += A_id`.
Less obvious: if A and B are joined by table AB, the primary key of table AB doesn't need to be added to A or B. Rails wants you to worry about the AB table as less as possible, so searches, builds, etc. can all be done by A.B or B.A instead of A.AB.B or B.AB.A

Resources