Rails 4 Associations: Help setting up database - ruby-on-rails

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])

Related

Ruby on Rails - How to replace one has_one association with another without :delete or :destroy

I have these two classes:
class Dungeon < ApplicationRecord
has_one :room
...
end
class Room < ApplicationRecord
belongs_to :dungeon
...
end
I want to be able to replace Dungeon's one Room object with another without actually deleting or destroying Dungeon's original Room object. When I try using update(room: r), I get
ActiveRecord::RecordNotSaved: Failed to remove the existing associated room. The record failed to save after its foreign key was set to nil.
What do I need to do to be able to simply replace Dungeon's one room with another?
Since Rails 5 belongs_to associations require the associated object to exist (see: PR introducing this behavior).
That said: If you want to keep a room although it does not have a dungeon associated you have to change your belongs_to definition to:
belongs_to :dungeon, optional: true

rails association limit record based on an attribute

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

How to change rails model object variable names that holds object of another model

Say I have this model and associated schema defined.
class Memory < ActiveRecord::Base
belongs_to :memory_slot
end
class MemorySlot < ActiveRecord::Base
has_many :memories
end
Now typically it let me can access memory slots of Memory via #memory.memory_slot.name. But I want to access it via different method like #memory.supporting_memory_slot.name. What is the best way I can do that?
You won't need any new migration, you can use the previous memory_slot_id, and still can change the name like following:
class Memory < ActiveRecord::Base
belongs_to :supporting_memory_slot, class_name: 'MemorySlot', foreign_key: 'memory_slot_id'
end
class MemorySlot < ActiveRecord::Base
has_many :memories
end
This way, if you had any records saved previously, they will work in the current scenario as well. But if you generate a new migration, the old records saved will not be accessible, because they were used using the foreign_key as memory_slot_id.
If you can change your model association like this
class Memory < ActiveRecord::Base
belongs_to :supporting_memory_slot, :class_name => 'MemorySlot', :foreign_key => 'supporting_memory_slot_id'
end
then you can do something like this
#memory.supporting_memory_slot.name
Note: In this case,you must generate a new migration to add supporting_memory_slot_id to your memories table

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

Ruby on Rails: belongs_to and has_many over the same pair on the same model

First, my model definitions:
class Certificate < ActiveRecord::Base
belongs_to :certificate_series
end
class CertificateSeries < ActiveRecord::Base
has_many :certificates
belongs_to :last_certificate_in_series, :class_name => "Certificate", :foreign_key => :last_certificate_in_series_id
end
I thought of the association as: CertificateSeries have many Certificates and have a Certificate which is the last one in the series. I've thought of using have_one, but according to the association basics, have_one is not correct based on where I put the associating key.
You can think of it as every time you get a new passport, the physical passport is changing (with its issuing and expiry date changing) but it is still a passport. Thus, you can have a lot of physical passports (certificates) but you only have 1 present passport (the last in the series, which is a physical passport) and the physical passports belong to the "passport" class of IDs (certificate series).
What I want is that I can call CertificateSeries.last_certificate_in_series and it will return the last certificate in the series. I thought of doing this in the db level by having the field last_certificate_in_series_id. I wanted to do it this way to reduce the database overhead of just getting the last one in the series. The other approach I thought of was get all the certificates in a series, arrange them by date, and get the most recent one.
Thus I'm now having problems trying to reflect this association in the models.
belongs_to :last_certificate_in_series, :class_name => "Certificate", :foreign_key => :last_certificate_in_series_id
seems not to define it properly. I can call CertificateSeries.last_certificate_in_series but it only returns a nilClass even when last_certificate_in_series_id is set to a valid value.
I'm open for suggestions on other approaches.
You might consider the acts_as_list plugin.
Then in the certificate model:
belongs_to :certificate_series
acts_as_list :scope => certificate_series
You should create a method that returns the last certificate in series. You don't need to create another relationship for this.
class Certificate < ActiveRecord::Base
belongs_to :certificate_series
end
class CertificateSeries < ActiveRecord::Base
has_many :certificates
def last_certificate_in_series
certificates.last
end
end
Actually you don't need the extra belongs_to association. It sounds very natural that the last certificate in the certificate_series.certificates is the one you want. So you could simply do this:
class Certificate < ActiveRecord::Base
belongs_to :certificate_series
end
class CertificateSeries < ActiveRecord::Base
has_many :certificates
end
certificate_series.certificates.last

Resources