Specify an optional reference in your Rails model
Asked Answered
B

3

20

I have a Sponsors model and a Promo Codes model.

  • A sponsor can have zero or more promo codes
  • A promo code can have zero or one sponsors

Thus a promo code should have an optional reference to a sponsor, that is, a sponsor_id that may or may not have a value. I'm not sure how to set this up in Rails.

Here's what I have so far:

# app/models/sponsor.rb
class Sponsor < ActiveRecord::Base
  has_many :promo_codes  # Zero or more.
end

# app/models/promo_code.rb
class PromoCode < ActiveRecord::Base
  has_one :sponsor  # Zero or one.
end

# db/migrate/xxxxx_add_sponsor_reference_to_promo_codes.rb
# rails g migration AddSponsorReferenceToPromoCodes sponsor:references
# Running migration adds a sponsor_id field to promo_codes table.
class AddSponsorReferenceToPromoCodes < ActiveRecord::Migration
  def change
    add_reference :promo_codes, :sponsor, index: true
  end
end

Does this make sense? I'm under the impression that I have to use belongs_to in my Promo Codes model, but I have no basis for this, just that I've haven't seen a has_many with has_one example yet.

Berretta answered 24/2, 2014 at 16:48 Comment(2)
What made this more confusing for me was the title of this Rails Guides association section: "Choosing Between belongs_to and has_one". I interpreted it as choosing has_one or belongs_to. The section is actually about using both; which goes in which model.Berretta
I also learned that belongs_to is always on the model with the foreign key.Berretta
F
3

This looks like a simple has_many and belongs_to relationship:

# app/models/sponsor.rb
class Sponsor < ActiveRecord::Base
  has_many :promo_codes  # Zero or more.
end

# app/models/promo_code.rb
#table has sponsor_id field
class PromoCode < ActiveRecord::Base
  belongs_to :sponsor  # Zero or one.
end

has_one isn't appropriate here, as it would replace has_many: ie, you either have "has_many" and "belongs_to" OR "has_one" and "belongs_to". has_one isn't generally used much: usually it is used when you already have a has_many relationship that you want to change to has_one, and don't want to restructure the existing tables.

Fajardo answered 24/2, 2014 at 16:55 Comment(1)
This answer was correct when it was written, but not anymore (but is still accepted and this might cause confusion). Not to steal correct answer, if you are reading this and using Rails 5+, check also answer by Paul Ardeleanu bellowAlverta
H
88

In Rails 5, belongs_to is defined as required by default. To make it optional use the 'optional' option :)

class User
  belongs_to :company, optional: true
end

Source: https://github.com/rails/rails/issues/18233

Hydrotherapy answered 31/3, 2016 at 10:34 Comment(2)
Thank you for this!Ogg
For good clarification, read this article. Very well-written: simple and understandable. blog.bigbinary.com/2016/02/15/…Beacon
F
3

This looks like a simple has_many and belongs_to relationship:

# app/models/sponsor.rb
class Sponsor < ActiveRecord::Base
  has_many :promo_codes  # Zero or more.
end

# app/models/promo_code.rb
#table has sponsor_id field
class PromoCode < ActiveRecord::Base
  belongs_to :sponsor  # Zero or one.
end

has_one isn't appropriate here, as it would replace has_many: ie, you either have "has_many" and "belongs_to" OR "has_one" and "belongs_to". has_one isn't generally used much: usually it is used when you already have a has_many relationship that you want to change to has_one, and don't want to restructure the existing tables.

Fajardo answered 24/2, 2014 at 16:55 Comment(1)
This answer was correct when it was written, but not anymore (but is still accepted and this might cause confusion). Not to steal correct answer, if you are reading this and using Rails 5+, check also answer by Paul Ardeleanu bellowAlverta
D
1

Unless you specify validation, relationships are optional by default.

The belongs_to is to tell rails the other half of the relationship between those two objects so you can also call @promo_code.sponsor and, vice versa, @sponsor.promo_codes.

Documentation answered 24/2, 2014 at 16:57 Comment(2)
So to be clear, the has_many in the Sponsors model allows you to do @sponsor.promo_codes, and the belongs_to in the Promo Codes model allows you to do @promo_code.sponsor?Berretta
Not correct anymore. Rails 5 keeps default to required: true, we need to pass optional: true to make it optionalBeacon

© 2022 - 2024 — McMap. All rights reserved.