has_one model association not enforcing 'one' in associated model? - ruby-on-rails

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.

Related

Multiple Associations With the Same Table (Many-to-Many) Ruby on Rails

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

Rails: Multiple relationships between two models

I'm creating a Database Migration in Rails 4.0.4, and I want to capture the following relationship:
A customer has many credit cards. A customer has only one default credit card.
and here's what I think it should look like.
class Customer < ActiveRecord::Base
has_many :cards
has_one :card # i.e. has one default card
end
class Card < ActiveRecord::Base
belongs_to :customer
end
Is this correct? If so, how does Rails know which relationship the belongs_to in the Card class refers to? If it's incorrect (and I'm guessing it is), please help me fix it.
I'd put the scope on the card's side, seems easier for me
class Customer < ActiveRecord::Base
has_many :card
end
class Card < ActiveRecord::Base
belongs_to :customer
scope :default, -> { where is_default: true }
end
default_card = customer.cards.default
Currently your code is enough to confuse Rails by having has_one :card and has_many :cards.You should be using class_name option provided specially for these type of associations.
Something like this should work for you
class Customer < ActiveRecord::Base
has_many :cards
has_one :default_card, :class_name => "Card"
end
foreign_key
To add to Pavan's answer, you'll need to use some sort of condition to determine which is the default card.
Because Rails' relational database structure relies on foreign_keys to pull the related data, you'll need to either assign the correct foreign_key for your default_card, or use a condition to find it:
#app/models/customer.rb
Class Customer < ActiveRecord::Base
has_one :default_card, -> { where default: true" },class: "Card", foreign_key: "customer_id"
end
This would rely on having the boolean column default in your cards table

more than one entry saved on has_one association rails

I am trying to create a has_one association among two model.
class User < ActiveRecord::Base
has_one :emergency_contact
end
class EmergencyContact < ActiveRecord::Base
belongs_to :user
end
when i try to test it through rails console more than one entries are saved for the emergency contact model for a single user. Although when i retrieve it using User.emergency_contact only the first entry is returned. When saving how can i make it to rollback for more than one entry
You can simply validate uniqueness of user_id column in EmergencyContact:
class EmergencyContact < ActiveRecord::Base
belongs_to :user
validates_uniqueness_of :user_id, allow_nil: true
end

How to create belongs_to connections in some cases, Rails 4

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.

How to access the tables for has_many through association

Say I have following association.
class Supplier < ActiveRecord::Base
has_one :account
has_one :account_history, through: :account
end
class Account < ActiveRecord::Base
belongs_to :supplier
has_one :account_history
end
class AccountHistory < ActiveRecord::Base
belongs_to :account
end
How can I access account table through supplier model
How can I access supplier table through account model
How can I access join table accounthistory without through other model
How can I add a field by migration to accounthistory and then get value from that field through supplier or account model
Surely this is too simplistic, but this what I'd propose:
1. #supplier.account
#supplier = Supplier.find(id)
#supplier.account #-> brings account data
2. #account.supplier
#account = Account.find(id)
#account.supplier #-> brings supplier data
3. #supplier.account_history
#supplier = Supplier.find(id)
#supplier.account_history #-> brings account_history
4. Migration
def change
add_column :account_histories, :your_column, :type, after: :column
end
You can deal with the join model / table directly (unlike HABTM's), meaning you will be able to create migrations as you wish. The above migration code shows how you can add a column to the table directly
Delegate
To access the data from that model, you're in luck
Because you're using a singular association (has_one / belongs_to), you should be able to delegate the call to another model:
#app/models/supplier.rb
Class Supplier < ActiveRecord::Base
has_one :account
has_one :account_history, through: :account
end
#app/models/account.rb
class Account < ActiveRecord::Base
belongs_to :supplier
has_one :account_history
delegate :extra, :vars, :here, to: :account_history
end
This will allow you to call: #supplier.account.extra etc
Hope this helps? Your question was rather scant on context, so I can fix my answer if you update!

Resources