rails association limit record based on an attribute - ruby-on-rails

As you can see in the schema below, a user can create courses and submit a time and a video (like youtube) for it, via course_completion.
What I would like to do is to limit to 1 a course completion for a user, a given course and based one the attribute "pov" (point of view)
For instance for the course "high altitude race" a user can only have one course_completion with pov=true and/or one with pov=false
That mean when creating course completion I have to check if it already exist or not, and when updating I have to check it also and destroy the previous record (or update it).
I don't know if I'm clear enough on what I want to do, it may be because I have no idea how to do it properly with rails 4 (unless using tons of lines of codes avec useless checks).
I was thinking of putting everything in only one course_completion (normal_time, pov_time, normal_video, pov_video) but I don't really like the idea :/
Can someone help me on this ?
Thanks for any help !
Here are my classes:
class CourseCompletion < ActiveRecord::Base
belongs_to :course
belongs_to :user
belongs_to :video_info
# attribute pov
# attribute time
end
class Course < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :courses
has_many :course_completions
end

You could use validates uniqueness with scoping Rails - Validations .
class CourseCompletion < ActiveRecord::Base
belongs_to :course
belongs_to :user
belongs_to :video_info
validates :course, uniqueness: { scope: :pov, message: "only one course per pov" }
# attribute pov
# attribute time
end

Related

Rails model associations - has_one or single table inheritance?

I'm having trouble deciding between Single Table Inheritance and a simple has_one relationship for my two models.
Background: I'm creating a betting website with a "Wager" model. Users may create a wager, at which point it is displayed to all users who may accept the wager if they choose. The wager model has an enum with three statuses: created, accepted, and finished.
Now, I want to add the feature of a "Favorite Wager". The point of this is to make it more convenient for users to create a wager, if they have ones they commonly create. One click instead of ten.
FavoriteWagers exist only as a saved blueprint. They are simply the details of a wager -- when the User wants to create a Wager, they may view FavoriteWagers and click "create", which will take all the fields of the FavoriteWager and create a Wager with them. So the difference is that FavoriteWagers acts as only as a storage for Wager, and also includes a name specified by the user.
I read up on STI, and it seems that a lot of examples have multiple subclassing - eg. Car, Motorcycle, Boat for a "Vehicle" class. Whereas I won't have multiple subclasses, just one (FavoriteWager < Wager). People have also said to defer STI until I can have more classes. I can't see myself subclassing the Wagers class again anytime soon, so that's why I'm hesitant to do STI.
On the other hand, has_one doesn't seem to capture the relationship correctly. Here is an example:
Class User < ApplicationRecord
has_many :favorite_wagers, dependent: :destroy
has_many :wagers, dependent: destroy
end
Class FavoriteWager < ApplicationRecord
has_one :wager
belongs_to: user, index: true, foreign_key: true
end
Class Wager < ApplicationRecord
belongs_to :favorite_wager, optional: true
belongs_to :user
end
I've also thought about just copying the fields directly, but that's not very DRY. Adding an enum with a "draft" option seems too little, because I might need to add more fields in the future (eg. time to auto-create), at which point it starts to evolve into something different. Thoughts on how to approach this?
Why not just do a join table like:
Class User < ApplicationRecord
has_many :favorite_wagers, dependent: :destroy
has_many :wagers, through: :favorite_wagers
end
Class FavoriteWager < ApplicationRecord
belongs_to :wager, index: true, foreign_key: true
belongs_to :user, index: true, foreign_key: true
end
Class Wager < ApplicationRecord
has_one :favorite_wager, dependent: destroy
has_one :user, through: :favorite_wager
end
Your FavoriteWager would have the following fields:
|user_id|wager_id|name|
That way you can access it like:
some_user.favorite_wagers
=> [#<FavoriteWager:0x00007f9adb0fa2f8...
some_user.favorite_wagers.first.name
=> 'some name'
some_user.wagers.first.amount
=> '$10'
some_user.wagers.first.favorite_wager.name
=> 'some name'
which returns an array of favorite wagers. If you only want to have ONE favorite wager per user you can tweak it to limit that. But this gives you the ability to have wagers and users tied together as favorites with a name attribute. I don't quite understand your use case of 'a live wager never has a favorite' but that doesn't matter, you can tweak this to suit your needs.

Update one model from another

I have two models interacting with one another. Each Course has_many Reservations, upon saving the reservation I want to update the Course :course_places attribute to minus the number of reservations that have just been made (equal to the remaining_places method I have created in the reservation controller). I hope that makes sense.
I have been trying to use this answer as inspiration but its not working because the Course update_places! method doesn't know which reservation it's referring to https://stackoverflow.com/a/19169367/6103550
Any suggestions would be warmly received! thanks
Error
undefined method `reservation' for #<Course:0x007f832c1f5020> Did you mean? reservations reservations=
Course Model
class Course < ApplicationRecord
belongs_to :listing, optional: true
has_many :reservations
def update_places!
self.update_column(:course_places, self.reservation.sum(:remaining_places))
end
end
Reservation Model
class Reservation < ApplicationRecord
belongs_to :user
belongs_to :course
belongs_to :listing
after_save :update_course_places
def update_course_places
self.course.update_places!
end
end
Hope this works
def update_places!
remaining_places = Reservation.where(course_id: self.id).sum(:remaining_places)
self.update_column(:course_places, remaining_places)
end

Rails 4 Associations: Help setting up database

I need some assistance with my Rails 4 associations. I have the following 4 models:
class User < ActiveRecord::Base
has_many :check_ins
has_many :weigh_ins, :through => :check_ins
has_many :repositionings, :through => :check_ins
end
class CheckIn < ActiveRecord::Base
belongs_to :user
has_one :weigh_in
has_one :repositioning
end
class Repositioning < ActiveRecord::Base
# belongs_to :user
belongs_to :check_in
end
class WeighIn < ActiveRecord::Base
# belongs_to :user
belongs_to :check_in
end
Question: If I am setup this way, how would I input repositionings and weigh_ins separately, but still have them linked through a single check in?
You would have to retain one of the other association's ID in order to make it work.
For example, let's say:
You have created a CheckIn.
You now add a Repositioning to that check in.
Store the ID of the repositioning object
When adding your WeighIn object, you would simply reference the correct CheckIn record: correct_checkin_record = CheckIn.where(repositioning: the_repositioning_id)
You can then add the WeighIn object to that particular record.
An alternative (and simpler) method would be to access the CheckIn directly through the User: correct_checkin_record = #user.checkin -- This would pull in the correct CheckIn every time.
I've included both options to help visualize exactly what is going on in the relation.
Do you want to have users input weigh_ins and repositionings on different pages?
Having weigh_ins and repositionings inputted separately but still be part of a single checkin is fine with that setup. Its just matter of getting the same check_in object and make the associations to that object, which can be done through the controller by passing in check_in ID params and do CheckIn.find(params[:id])

Specify an optional reference in your Rails model

I have a Sponsors model and a Promo Codes model.
A sponsor can have zero or more promo codes
A promo code can have zero or one sponsors
Thus a promo code should have an optional reference to a sponsor, that is, a sponsor_id that may or may not have a value. I'm not sure how to set this up in Rails.
Here's what I have so far:
# app/models/sponsor.rb
class Sponsor < ActiveRecord::Base
has_many :promo_codes # Zero or more.
end
# app/models/promo_code.rb
class PromoCode < ActiveRecord::Base
has_one :sponsor # Zero or one.
end
# db/migrate/xxxxx_add_sponsor_reference_to_promo_codes.rb
# rails g migration AddSponsorReferenceToPromoCodes sponsor:references
# Running migration adds a sponsor_id field to promo_codes table.
class AddSponsorReferenceToPromoCodes < ActiveRecord::Migration
def change
add_reference :promo_codes, :sponsor, index: true
end
end
Does this make sense? I'm under the impression that I have to use belongs_to in my Promo Codes model, but I have no basis for this, just that I've haven't seen a has_many with has_one example yet.
In Rails 5, belongs_to is defined as required by default. To make it optional use the 'optional' option :)
class User
belongs_to :company, optional: true
end
Source: https://github.com/rails/rails/issues/18233
This looks like a simple has_many and belongs_to relationship:
# app/models/sponsor.rb
class Sponsor < ActiveRecord::Base
has_many :promo_codes # Zero or more.
end
# app/models/promo_code.rb
#table has sponsor_id field
class PromoCode < ActiveRecord::Base
belongs_to :sponsor # Zero or one.
end
has_one isn't appropriate here, as it would replace has_many: ie, you either have "has_many" and "belongs_to" OR "has_one" and "belongs_to". has_one isn't generally used much: usually it is used when you already have a has_many relationship that you want to change to has_one, and don't want to restructure the existing tables.
Unless you specify validation, relationships are optional by default.
The belongs_to is to tell rails the other half of the relationship between those two objects so you can also call #promo_code.sponsor and, vice versa, #sponsor.promo_codes.

Add model atribute to result of a query

I have three models and they look like (simplified):
class Airline < ActiveRecord::Base
attr_accessible :name
has_many :airplanes
has_many :airplane_switches
end
class Airplane < ActiveRecord::Base
attr_accessible :airline_id, :register
belongs_to :airline
has_many :airplane_switches
end
class AirplaneSwitch < ActiveRecord::Base
attr_accessible :airline_id, :airplane_id
belongs_to :airplane
belongs_to :airline
end
Airplanes could have been in some Airlines, so I needed another model that indicates if an Airplane was in one or more Airlines.
I am building a form to let users upload some info about an Airplane, they just select the airplane register (callsign) and then they will get a list to choose in which Airline it was.
This will work over an AJAX request. But, I am trying to figure out how to show the Airline name from my controller, to avoid another AJAX call by fetching another JSON file just to get the name of the Airline based on the airline_id in AirplaneSwitch.
#airplane = Airplane.find_by_register(params[:register])
#airplane_switches = #airplane.airplane_switches # Here I need to join also each Airline.name
I think this way would be more efficient, but I have no idea if it's possible to do.
This should work:
#airplane.airplane_switches.select('*, airlines.name as airline_name').joins(:airline)
Let's say you have variable airplane_switch that contains AirlineSwitch instance fetched in that way. All you need to do to get your airline name is:
airplane_switch.airline_name

Resources