Hi quick question on Rails belongs_to association.
In an example we have to models Foo & Hah.
class Foo < ActiveRecord::Base
end
class Hah < ActiveRecord::Base
belongs_to :foo
end
Standard stuff. But my questions comes in. How do I create the code for where every record of model Hah has an foo_id but not every record of model Foo is associated with a Hah model in this way.
I.e.
## Table foos ##
id post
1 stuff
2 dancing
3 now
## Table hahs ##
id line a_id
1 fill 2
3 this 3
Thanks for any help
the question is a little bit vague due to the naming of the Foo and Huh but here is the best i can answer to this...
as described by the Active Record Documentation
2.1) A belongs_to association sets up a one-to-one connection with another model, such that each instance of the declaring model "belongs to" one instance of the other model. For example, if your application includes customers and orders, and each order can be assigned to exactly one customer, you'd declare the order model this way:
class Order < ActiveRecord::Base
belongs_to :customer
end
2.2) 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. For example, if each supplier in your application has only one account, you'd declare the supplier model like this:
class Supplier < ActiveRecord::Base
has_one :account
end
2.7) Choosing Between belongs_to and has_one
If you want to set up a one-to-one relationship between two models, you'll need to add >belongs_to to one, and has_one to the other. How do you know which is which?
The distinction is in where you place the foreign key (it goes on the table for the class declaring the belongs_to association), but you should give some thought to the actual meaning of the data as well. The has_one relationship says that one of something is yours - that is, that something points back to you. For example, it makes more sense to say that a supplier owns an account than that an account owns a supplier. This suggests that the correct relationships are like this:
class Supplier < ActiveRecord::Base
has_one :account
end
class Account < ActiveRecord::Base
belongs_to :supplier
end
and about having zero or one relation
I think the "zero or one" relation you're looking for is the has_one relation. This relation is not mandatory (unless you add a validation to it).
also check this question... typically the Account class will look like the following, if every account is required to have a valid supplier
class Account < ActiveRecord::Base
belongs_to :supplier
validates_presence_of :supplier
end
I think you're looking for something like this:
Migration:
def change do
create_table :foos do |t|
t.string :post
t.timestamps
end
create_table :hahs do |t|
t.references :foo, null: false
t.string :line
t.timestamps
end
end
Models:
class Foo < ActiveRecord::Base
has_many :hahs
end
class Hah < ActiveRecord::Base
belongs_to :foo
validates :foo, presence: true
end
The null: false bit in the hahs table will ensure that a Hah cannot be created without belonging to a Foo on the database level, and the validates line ensures that ActiveRecord will not allow the creation of a Hah that doesn't belong to a Foo on the Ruby level.
Related
I have the following
class User < ApplicationRecord
has_one :physician
end
and
class Physician < ApplicationRecord
belongs_to :user
end
Since a user can only have one physician, I was surprised that multiple physician records can be created with the same user id.
Why is this? And how to I prevent it?
A belongs_to association does not in any way guarantee that the values are unique. It only stipulates that the association is stored in a foreign key on this models table and thus can only have a single value.
has_one doesn't actually provide any guarantees either. It just specifies that this table is referred to on the other table. If there are multiple matching rows on the other table it will chose the last.
If you want to enforce uniqueness per table you need a validation:
class Physician < ApplicationRecord
belongs_to :user
validates_uniqueness_of :user_id
end
And a database index to actually guarantee uniqueness on the database level:
class AddUniqueIndexToPhysicians < ActiveRecord::Migration[6.0]
def change
add_index :physicians, :user_id, unique: true
end
end
Physician probably shouldn't have a user_id associated to it:
class User
belongs_to :physician # has a physician_id column
end
class Physician
has_many :users # has no mention of user_id in its schema
end
belongs_to is required by default for Rails 5 and later. If users can onboard without a physician and you need to disable this:
class User
belongs_to :physician, optional: true
end
And then later, validate the presence of physician only after some condition.
I am trying to figure out the best way to accomplish a relationship and having some trouble. I have come across a few articles that somewhat address what I am looking to do but not quite.
I have a Users model and a Tickets model. Both are joined through a UserTickets model so I am able to assign multiple Users to a ticket. What I would like to do is segment the Users assigned to a Ticket into requesters and agents. The Users model does not have any columns to declare if the user is a requester or agent, rather I have is_admin, is_customer, etc. I think what I need is along the lines of Ruby On Rails - many to many between the same table but not quite.
Ideally, I'd like to have my Tickets table take agent_id's (user_id from User class) and requester_id's (user_id from User class) rather than the general user_id's (user_id from User class which combines all the users into one group). I would assume would still allow me to call #current_user.tickets to pull all the tickets that are assigned to that user.
Here is my Users model:
has_many :user_tickets
has_many :support_tickets, through: :user_tickets
Here is my Tickets model:
has_many :user_tickets
has_many :users, through: :user_tickets
Here is my UserTickets join model:
belongs_to :user
belongs_to :support_ticket
Your help is greatly appreciated!
This is not a duplicate.
#Skylar,
1) If your goal is to assign multiple Users to a ticket? Because this doesn't require a many-to-many relationship. You just need to a one-many relationship and a boolean attribute agent? on User model. You can even create am Agent model that uses User model, if you like this better.
2) However, if you wanted to create a many-to-many relationship check out this. The Rails documentation is better than I write. See section 2.6 The has_and_belongs_to_many Association.
Solution to 1)
Models
class User < ApplicationRecord
has_many :tickets, class_name: :Ticket, foreign_key: :agent_id
def agent?
self.agent
end
end
class Agent < User
default_scope { where(agent: true) }
end
class Ticket < ApplicationRecord
belongs_to :user
belongs_to :agent, class_name: :User
end
Migrations
class CreateTickets < ActiveRecord::Migration[5.0]
def change
create_table :tickets do |t|
t.references :user, index: true
t.references :agent, index: true
t.string :event_name
t.timestamps
end
end
end
I was following an online tutorial on ActiveRecord where the instructor writes the following code when defining a table and its relationship:
#Setup of the database table
class CreateTimeEntries < ActiveRecord::Migration
def change
create_table :time_entries do |t|
t.float :time
t.belongs_to :customer
t.belongs_to :employee
t.timestamps
end
end
end
#Relationship definition in the relevant model
class TimeEntry < ActiveRecord::Base
belongs_to :customer
belongs_to :employee
end
Aren't those lines redundant ?
#in table setup
t.belongs_to :customer
t.belongs_to :employee
#in the relevant model
belongs_to :customer
belongs_to :employee
I understood that the lines in the db table setup are here to define foreign keys, how come then that we need to define the relationship in the model as well?
I thought foreign keys were by themselves defining such relationship.
What am I missing here? Can't find any clear answer on the web. Thanks a lot.
Those are two completely different methods:
Within migration belongs_to :parent only creates parent_id column - it is only executed when you run migration. No foreign key is defined here - rails do not believe those are needed. This is just a syntatic sugar for:
t.integer :parent_id
After migrations are run, all rails know is that your TimeEntry model has parent_id column - it has absolutely no other meaning. This is why you have to give it the meaning with belongs_to - this method executed in context of ActiveRecord objects will create the association - it will create a method parent with all the arel power behind it to fetch referenced object as well as provide some validation and save hooks to make working with the object easier. It is not always necessary to have this defined.
In short, without belongs_to you would only be able to call my_model.parent_id (provided by belongs_to in migration), but not my_model.parent
They have different goals. The migartion define de db table and allow create or modify the table. The model define to the Rails framework how to work with db tables.
You must define your models like this:
class Customer < ActiveRecord::Base
has_many :time_entries
end
class Employe < ActiveRecord::Base
has_many :time_entries
end
class TimeEntry < ActiveRecord::Base
belongs_to :customer
belongs_to :employe
end
At the migration level, belongs_to :customer add a field customer_id that relate models.
I'm trying to wrap my head around how I should model my database for a parent model with many has_one associations (20+). I have one model called House which is the parent model of all the other models:
class House < ActiveRecord::Base
has_one :kitchen
has_one :basement
has_one :garage
has_one :common_room
#... Many other child models
end
All the child models contain unique properties specific to their own class. I've thought about STI, but there isn't really any shared functionality or inputs that I can use across models. I've also thought of making one 'super model', but that doesn't really follow Rails best practices, plus it would contain over 200 columns. Is there another design pattern or structure that I can use to model this efficiently so that I can reduce database calls?
class House
has_many :rooms
Room::TYPES.each do |room_type|
has_one room_type, -> { where(room_type: room_type) }, class_name: "Room"
end
class Room
belongs_to :house
TYPES = %i/kitchen basement garage common_room etc/.freeze
end
In your migration make sure to add_index :rooms, [:type, :house_id], unique: true. Unless a house can have more than 1 type of room. If that is the case, I think a different approach is needed.
To your second question, it depends really, what type of database are you using? If its PostgreSQL you could use hstore and store those as a properties hash. Or you could serialize you db to take that as a hash. Or you could have another model that room has many of. For instance has_many :properties and make a property model that would store that information. Really depends on what else you want to do with the information
Why not using Polymorphism in Rails? It would be much simpler
class House < ActiveRecord::Base
has_many :properties, as: :property_house
end
class Kitchen < ActiveRecord::Base
belongs_to :property_house, polymorphic: true
end
class Garage < ActiveRecord::Base
belongs_to :property_house, polymorphic: true
end
For more information, go here: http://terenceponce.com/blog/2012/03/02/polymorphic-associations-in-rails-32/
I have three relevant models:
class InventoryItem < ActiveRecord::Base
belongs_to :item, :foreign_key => :item_id
belongs_to :vendor
has_many :shopping_list_items
class ShoppingList < ActiveRecord::Base
has_many :shopping_list_items
belongs_to :user
end
class ShoppingListItem < ActiveRecord::Base
belongs_to :shopping_list
belongs_to :inventory_item
end
What I am trying to do is create a sidebar shopping list that will autoupdate ShoppingListItem attributes (specifically price) when a respective attribute is changed in the InventoryItem table (again, price). My thinking was to have these three classes and map ShoppingListItems directly to InventoryItems, but I'm unsure of how to proceed with that. Alternatively, is it possible to do away with the ShoppingListItem class entirely and make ShoppingList be a collection of InventoryItems specified by the user? Any input is much appreciated. Thanks in advance!
To redo my comments as a real answer, yes, it is possible to forego the ShoppingListItem model in this case, as long as you don't need to attach any data to that model itself (e.g. the time the item was added to the list). You could link your models as follows with a has_and_belongs_to_many association:
class InventoryItem < ActiveRecord::Base
belongs_to :item
belongs_to :vendor
has_and_belongs_to_many :shopping_lists
end
class ShoppingList < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :inventory_items
end
This will allow you to assign an array of inventory items to the inventory_items attribute of a shopping list, and Rails will create or delete the necessary join records automatically. More information from the Rails guides. Note that you'll still need a join table in your schema -- there just isn't a model associated with it. In your case, the migration might look like this:
create_table :inventory_items_shopping_lists, id: false do |t|
t.references :inventory_item
t.references :shopping_list
end
add_index :inventory_items_shopping_lists, :inventory_item_id
add_index :inventory_items_shopping_lists, :shopping_list_id
add_index :inventory_items_shopping_lists, [:inventory_item_id, :shopping_list_id], unique: true
Note that in order for Rails to auto-detect the table, its name should be the combined plural forms of both models in alphabetical order. Otherwise you need to specify the table name using the join_table option when defining the association.