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.

Dennis
  • 56,821
  • 26
  • 143
  • 139
  • What made this more confusing for me was the title of this Rails Guides association section: "[Choosing Between belongs_to and has_one](http://guides.rubyonrails.org/association_basics.html#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. – Dennis Feb 24 '14 at 17:59
  • 1
    I also learned that `belongs_to` is always on the model with the foreign key. – Dennis Feb 24 '14 at 18:01

3 Answers3

86

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

Paul Ardeleanu
  • 6,620
  • 2
  • 40
  • 41
  • Thank you for this! – Torrm May 30 '17 at 17:03
  • For good clarification, read this article. Very well-written: simple and understandable. https://blog.bigbinary.com/2016/02/15/rails-5-makes-belong-to-association-required-by-default.html – ARK Dec 18 '19 at 15:59
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.

Max Williams
  • 32,435
  • 31
  • 130
  • 197
  • 8
    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 bellow – zmilojko Feb 27 '18 at 12:22
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.

maxshelley
  • 166
  • 2
  • 5
  • 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`? – Dennis Feb 24 '14 at 17:13
  • 1
    Not correct anymore. Rails 5 keeps default to `required: true`, we need to pass `optional: true` to make it optional – ARK Dec 18 '19 at 16:01