Does Rails have a way to handle has_one join tables? - ruby-on-rails

Consider the following model setup:
Model A
has one B1, type: B
has one B2, type: B
Model B
has many A
I want to be able have this work:
class Motorcycle < ActiveRecord::Base
has_one :front_tire, class_name: "Tire"
has_one :back_tire, class_name: "Tire"
end
class Tire < ActiveRecord::Base
has_many :motorcycles
end
The end result would be me being able to do this:
m = Motorcycle.new
ft = Tire.new
bt = Tire.new
m.front_tire = ft
m.back_tire = bt
m.save
Tire.first.motorcycles #=> [...]

You can't have has_many paired with has_one, has_* needs to be paired with belongs_to (of course with exception of has_many :through).
So, you need to either change to motorcycle belongs_to :front_tire or create a third join model.

I think you're looking for basic Single-table Inheritance, as front tires and back tires really aren't the same thing, but rather specific types of tires. To facilitate this, you would need to add a type string column to your tires table, and declare two sub-classes of the Tire class:
class Motorcycle < ActiveRecord::Base
belongs_to :front_tire
belongs_to :back_tire
end
class Tire < ActiveRecord::Base
end
class FrontTire < Tire
has_many :motorcycles
end
class BackTire < Tire
has_many :motorcycles
end
This would allow you to use Tire.first, which would return either an instance of FrontTire or BackTire, which would have many motorcycles. This fulfills your Tire.first.motorcycles requirement.
m = Motorcycle.new
ft = FrontTire.new # id 1
bt = BackTire.new # id 2
m.front_tire = ft
m.back_tire = bt
m.save
Tire.first.motorcycles # returns FrontTire #1
# Or, find specifically by tire type
FrontTire.first.motorcycles # all motorcycles with this front-tire
BackTire.first.motorcycles # all motorcycles with this back-tire
Alternatively, you could simply use a generic tires relationship, now that front and back tires are different classes:
class Motorcycle
has_many :tires
end
class Tire < ActiveRecord::Base
end
class FrontTire < Tire
has_many :motorcycles, foreign_key: :tire_id
end
class BackTire < Tire
has_many :motorcycles, foreign_key: :tire_id
end
Npw Motorcycle.first.tires would return an array of two objects, one instance of a FrontTire and one instance of a BackTire. You would likely want to add a validator to prevent multiple front/back tires being assigned to the same motorcycle.

This is not tested at all but how about something like this:
has_many :motorcycles,
:class_name => 'Motorcycle',
:finder_sql => proc { "select * from motorcycles
where front_tire_id = #{id} OR
back_tire_id = #{id}" }

I think something like this should work
class Tire < ActiveRecord::Base
belongs_to :motorcycle
end
change the has_many :motercycles to belongs_to :motorcycle
Motorcycle has many tires (through has_one)
Tires belong to motorcycle
or you could have used something like
class Motorcycle < ActiveRecord::Base
has_many :tires
end
class Tire < ActiveRecord::Base
belongs_to :motorcycle
end
with Tire containing a column position wich may hold values for front or back
you could create some constants in model to maintain those like
class Tire < ActiveRecord::Base
FROUT 1
BACK 2
#....
end
This is just an another option :)

I managed to solve it with this set of code:
class Motorcycle < ActiveRecord::Base
has_many :tire_brands
has_one :front_tire, class_name: "Tire"
has_one :back_tire, class_name: "Tire"
end
class Tire < ActiveRecord::Base
belongs_to :motorcycle
belongs_to :tire_brand
end
class TireBrand < ActiveRecord::Base
has_many :tires
has_many :motorcycles, through: :tires
end

Related

how to connect two table that has no relation in rails

I have a table called foods and categories, but this table has no relation, I want to connect them through another table called food_category. And I want to make one-to-one relation between food and category maybe the diagram looks like this
class Food < ApplicationRecord
has_one :category
end
class Category < ApplicationRecord
has_one :food
end
class FoodCategory < ApplicationRecord
belongs_to :category
belongs_to :food
end
Is it possible to do this?
Yes, this is possible. You just need to do
has_one :category, through: :food_categories
as discussed in the Rails docs here.
However, this is a long-winded way to go about this kind of association. If it's going to be one-to-one, why not just add a foreign key to Category from Food? And presumably, you would actually want Category to contain many Food records? Seems like the below would make more sense:
class Food < ApplicationRecord
belongs_to :category
end
class Category < ApplicationRecord
has_many :food
end
class Food < ApplicationRecord
has_one :food_category
has_one :category, through: :food_categories
end
In rails console, you can access like this
Food.find(:id).categories

How to model table with items which have multiple subcategories?

I need some advice. I am pretty new in modeling database so I was wondering what would be the most convenient way to model database which would make my life easier. I have one Car item which can have multiple descriptive subcategories, like TireCategory, GlassCategory, EngineCategory...
I was thinking about doing something like this:
class Car < ActiveRecord::Base
belongs_to :tire_category
belongs_to :glass_category
belongs_to :engine_category
#Car would have three additional columns: tire_category_id, glass_category_id, engine_category_id
end
class TireCategory < ActiveRecord::Base
has_many :cars
end
class GlassCategory < ActiveRecord::Base
has_many :cars
end
class EngineCategory < ActiveRecord::Base
has_many :cars
end
Is this recommended way to do it or some other relation would be more suitable? Thank you!
You can store cars (with uniq data like model name and sku) in one table, properties (like engines, glasses) in another, and one more table for relation between cars and properties.
class Car < ActiveRecord::Base
has_many :car_properties, dependent: :destroy
has_many :properties, through: :car_properties
end
class Property < ActiveRecord::Base
has_many :car_properties, dependent: :destroy
has_many :cars, through: :car_properties
end
class CarProperty < ActiveRecord::Base
belongs_to :car
belongs_to :property
end
If you have a lot of properties and want to logically separate them then create separate models for each property. Like Engine, Tire, Glass. Each model will contain properties variants. Then create one table for each property to connect it to the car. Like CarEngine, CarGlass. It will contain car_id and property_id (engine_id, glass_id).

Find 'homeless families' in many-to-many relation

I have 2 models and one linking them in many-to-many relation.
Like this:
class Family < ActiveRecord::Base
has_many :family_in_house
end
class House < ActiveRecord::Base
has_many :family_in_house
end
class FamilyInHouse < ActiveRecord::Base
belongs_to :family
belongs_to :house
end
And I need to have a separate scope for families that are not associated with any house.
I'm realtively new to RoR and couldn't find the solution myself. I use Rails 3.2.9 if it's necessary.
Thanks in advance!
class Family < ActiveRecord::Base
has_many :family_in_houses
scope :without_house, includes(:family_in_houses).where( :family_in_houses => {:house_id=>nil} )
end

Creating models with two words

I have two models one is named BusinessUser and the other is named BusinessPlace.
The BusinessUser can have many BusinessPlaces
class BusinessUser < ActiveRecord::Base
has_many :BusinessPlaces
end
class BusinessPlace < ActiveRecord::Base
belongs_to :BusinessUser
end
When i'm trying to access #business_user.BusinessPlaces.count the sql that get build and run on DB is
SELECT COUNT(*) FROM "business_places" WHERE "business_places"."business_user_id" = 1
but in the migration and in the database the column for business user id is BusinessUser_id which makes the query to fail. Why the sql gets to be build wrong ? I've used the console to create the models.
You just need to set the foreign_key the association will be using for :business_user:
class BusinessUser < ActiveRecord::Base
has_many :business_places
end
class BusinessPlace < ActiveRecord::Base
belongs_to :business_user, :foreign_key => 'BusinessUser_id'
end
You're using the wrong wording for the keys. Your models should look like so:
class BusinessUser < ActiveRecord::Base
has_many :business_places
end
class BusinessPlace < ActiveRecord::Base
belongs_to :business_user
end
so basically use :business_places instead of :BusinessPlaces
if you use migrations to set up your databases you should not need to modify the foreign keys

Relation between two single-table inheritance models

I have the following two models
class ContactField < ActiveRecord::Base
end
class Address < ContactField
end
class Phone < ContactField
end
and
class Contact < ActiveRecord::Base
end
class Company < Contact
end
class Person < Contact
end
I want one contact, no matter is it Company or Person, to have many ContactFields(Addresses and Phones)... So where should I put those has many and belongs to?
Thanks
You already said it in plain english :-)
I want one contact, no matter is it Company or Person, to have many ContactFields(Addresses and Phones)... So where should I put those has many and belongs to? Thanks
class Contact < ActiveRecord::Base
has_many :contact_fields
end
class ContactField < ActiveRecord::Base
belongs_to :contact
end
This Relationship will be inherited by both address and phone
Looks like you're describing a belongs to relationship. The associations should be defined in the parent class, so they can be inherited by the subclasses.
class ContactField < ActiveRecord::Base
belongs_to :contact
belongs_to :company, :foreign_key => :contact_id
belongs_to :person, :foreign_key => :contact_id
end
class Contact < ActiveRecord::Base
has_many :contact_fields
has_many :addresses
has_many :phones
end
However #contact.contact_fields will just return ContactField records. If you need the methods defined in any of the sub classes you can always use the becomes method. There are a few ways around that. Such adding the extra associations, like I did. Or using ActiveRecord::Base#becomes

Resources