Imagine the following situation:
I have a dog model and a house model. A dog can belong to a house, and a house can have many dogs, so:
Class Dog < ActiveRecord::Base
belongs_to :house
end
Class House < ActiveRecord::Base
has_many :dogs
end
Now, imagine that I also want to create dogs that don't have a house. They don't belong to house. Can I still use that relationship structure and simply don't inform a :house_id when creating it?
Is there a better practice?
Obs.: I used this analogy to simplify my problem, but my real situation is: I have a model a user can generate instances of it. He can also create collections of those instances, but he can leave an instance outside a collection.
Be careful with this in Rails 5...
#belongs_to is required by default
From now on every Rails application will have a new configuration
option config.active_record.belongs_to_required_by_default = true, it
will trigger a validation error when trying to save a model where
belongs_to associations are not present.
config.active_record.belongs_to_required_by_default can be changed to
false and with this keep old Rails behavior or we can disable this
validation on each belongs_to definition, just passing an additional
option optional: true as follows:
class Book < ActiveRecord::Base
belongs_to :author, optional: true
end
from: https://sipsandbits.com/2015/09/21/whats-new-in-rails-5/#belongs_toisrequiredbydefault
I think it is absolutely normal approach.
You can just leave house_id with null value in database for the models which don't belong to other.
Related
I currently have the following model structure and relations
class Fleet < ApplicationRecord
# properties id, created_at, updated_at
# A fleet should only consists of the same type, either a fleet or cars or a fleet of trains. Never both
has_many :cars
has_many :trains
end
class Car < ApplicationRecord
# properties id, created_at, updated_at, number_of_gears, is_sports_car
belongs_to :fleet
end
class Train < ApplicationRecord
# properties id, created_at, updated_at, number_of_wagons, people_capacity, has_first_calass_section
belongs_to :fleet
end
The problem i have is thata fleet should only consists of the same type, either a fleet of cars or a fleet of trains. Never both.
With the current relations on fleet, it seems error prone to have both has_many associations (or more coming like Airplanes) in terms of integrity and ease of use when calling #fleet._cars-or-trains, i would have to know what i can call.
STI is not an option since the properties are very different on a Car and a Train. There are many more properties, for the sake of simplicity i have shortened them in this example.
What is the right way in Rails to do this?
I would use two different models: CarsFleet and TrainsFleet. Both can extend a Fleet model but define a different has_many association.
Then "Car" would belong to a cars_fleet and "Train" would belong to a trains_fleet.
This is what polymorphic relationships are for.
A Fleet could have many vehicles, and a Vehicle could be either a Car or a Train and, importantly, never both.
class Fleet < ApplicationRecord
belongs_to :vehicles, polymorphic: true # notice the belongs_to, it's the only way polymorphism works from memory
end
class Car < ApplicationRecord
has_one :fleet, as: :vehicle
end
class Train < ApplicationRecord
has_one :fleet, as: :vehicle
end
That way you can call fleet.vehicles and not worry about the class.
This code is untested but it should get you across the line, and it's the most Railsy-way to solve your issue.
You could add a model validation to check this:
class Fleet < ApplicationRecord
has_many :cars
has_many :trains
validates :cars, absence: true, if: -> { trains.any? }
validates :trains, absence: true, if: -> { cars.any? }
end
The above should stop fleets from being saved when they have both cars and trains present. However you can still create cars and trains separately and link them to a fleet.
# assuming a valid fleet exist
fleet = Fleet.create # should save
# you can still create both cars and trains
fleet.cars.create # should save
fleet.trains.create # should save
# however when you retry to save the fleet it fails on the validation
fleet.save # should fail
In the above scenario we would like to also fire the fleet validations when creating a car or train. To do this add a validates_associated:
validates_associated :fleet
In both the Car and Train model. This now should withhold the above car and train to be saved.
Note that this answer doesn't prevent anything SQL related and records are still technically allowed in the database. If you don't want this you have to build a trigger that runs AFTER INSERT and AFTER UPDATE. Since this is SQL related I would suggest asking a new question if you have any issues with trigger creation. Mention the database used since the trigger syntax is not the same for all database types (MySQL, SQL Server, ...).
I have a model with a var reference to another one.
User -> Profile
When I have generated the Profile model I used the references
feature so it has generated the corresponding migration
....
t.references :user
....
My question is do I have to add a relationship on the User model too?
has_one :Profile
Yes, you need both code in two models and the migration you mentioned.
class User < AR
has_one :profile
end
class Profile < AR
belongs_to :user
end
has_one and belongs_to are just methods which adds some more methods to your model. This means, you can have belongs_to defined on one model and no has_one on the other. The only problem is that you would be able to call profile.user, but no user.profile.
It is absolutely up to you which methods you want to be defined and which you don't need. If you never ever want anyone to call profile.user, but want user.profile just call has_one :profile. In general those method shares nothing except that their using same foreign key column.
It is however worth mentioning, that this is usually advised to declare reverse association - it is not needed for things to work though.
RailsGuides says:
http://guides.rubyonrails.org/association_basics.html
A has_many "association indicates that each instance of the model has zero or more instances of another model."
"A has_one association also sets up a one-to-one connection with another model, but with somewhat different semantics (and consequences). This association indicates that each instance of a model contains or possesses one instance of another model."
Does that mean if I want to set up an association that each instance of the model has zero or one instance of another model, the best way is to use has_many and not has_one? What will be the problems I'll encounter if I use has_one?
Thanks.
has_one is correct - the relationship that's set up is not mandatory unless you add your own validations to it.
To make it a bit clearer -
class Post < ActiveRecord::Base
has_one :author
end
class Author < ActiveRecord::Base
belongs_to :post
end
With no validations, a given post can have an author (but not more than one) - however an author is not necessary.
Unless you define specific validations, has_one just prevents you from having more than one object associated to your model.
Zero is ok.
I recently posted this question: How to model a many self-referential relationship with many parents?
I got an adequate answer, but I was told that I could model this better using Mongo/Mongoid. I always wanted to attempt using a different database paradigm so maybe this will be a good start.
Is it true that this would be easier to model using Mongo? If so, could you help lay it out for me?
I basically have two options right? 1 is to create an array through a document that references Skill id's through it? E.g, Skill.prerequisites = [Skill1, Skill2, Skill3]. Something that works with that structure right?
Otherwise it may be that I need to run something logical on that association so I'd have to create a separate model. This is where I get confused. Given I may need to create a separate model, does that exist as a document or an embedded document? What are my limitations w/ each strategy?
OK, so let's go with your skill approach. Here's how I'd model it in Mongoid:
class Skill
include Mongoid::Document
field :prereqruisite_skill_ids, type: Array, default: []
def prereqruisite_skills
Skill.any_of(:_id => prereqruisite_skill_ids).all
end
def add_prereqruisite!(skill)
self.prereqruisite_skill_ids << skill
safely.save!
end
end
With Mongo, this is the only way I would do it.
With ActiveRecord, you could:
class Skill < AR::Base
has_many :skill_prereqruisites
has_many :prerequisites, through: skill_prereqruisites, class_name: "Skill"
end
class SkillPrereqruisite < AR::Base
belongs_to :skill_one, foreign_key: "skill_one_id"
belongs_to :skill_two, foreign_key: "skill_two_id"
end
I'm having trouble figuring out the right way to set this association up.
I have 3 models: Musicians, Bands and Managers. Each of those can have a certain "level" associated with them (stress, happiness, etc).
But I'm not sure how to correctly associated my Levels model with the other 3.
Do I need some sort of has_many :through that's polymorphic? And how on earth do I set that up? Is there some other type of associated I need?
If this is the case where you're defining attributes that can be assigned to a particular class of model, then you probably want to use a more conventional approach. Instead of kind use something like record_type and build a scope on that instead.
That way what you'd do to fetch them is create an accessor method, not a relationship, between any of your entities and this column. Something like this:
def levels
Level.for_record_type(self.class)
end
The for_record_type is a scope:
scope :for_record_type, lambda { |record_type|
where(:record_type => record_type.to_s)
}
There's no convention for associating models with classes instead of instances of other models.
Yep, this would be a polymorphic association:
class Level < ActiveRecord::Base
belongs_to :leveled, :polymorphic => true # maybe there's a better word than "leveled"
end
class Musician < ActiveRecord::Base
has_many :levels, :as => :leveled
end
You'll need to add a leveled_type and leveled_id to your Levels table.