has_one from model attributes instead of foreign key

78 views Asked by At

I would like to have a has_one relation based on my Product's attributes rather than its id.

class Pricing
  belongs_to :type
  belongs_to :brand
  validates :type_id, uniqueness: { scope: :brand_id }
end


class Product
  belongs_to :type
  belongs_to :brand

  has_one :pricing # I need to indicate that the matching pricing is the one that has the same brand and type than my product.
end

How could we build the has_one relations in this scenario?

2

There are 2 answers

0
zaphodbln_ On BEST ANSWER

OK - to sum it from the comments I understand your logical data model as following:

Product <<- The Product in the shop

  • Type
  • Brand
  • Price <<- to be charged to the customer

Pricing <<- Reference for a comparable new product

  • Type
  • Brand
  • Reference Price

In this case you can try to use the composite key feature as mentioned by max. So you might try:

class Product
  belongs_to :type
  belongs_to :brand

  has_one :pricing, query_constraints: [:type_id, :brand_id]
end

Please be aware that you need Rails 7.1 for this. As this is a new feature expect to have a change in the syntax in one of the next releases :)

However, you may want to rethink your design. From my understanding you could combine both entities:

Product

  • Type
  • Brand
  • Price <<- to be charged to the customer
  • Reference Price <<- of the new product

Or you factor out Brand and Type into a single entity and use the through-option (https://guides.rubyonrails.org/association_basics.html#choosing-between-has-many-through-and-has-and-belongs-to-many --> similar for has_one)

0
smathy On

Adding the has_one API for such a scenario doesn't really make sense, the contortions that would be required in the Rails code to support this unusual requirement would massively overcomplicate the code for the 99.9% case.

It seems unlikely that you want anything other than the getter, so just write that yourself:

def pricing = Pricing.find_sole_by type: type, brand: brand