How do you structure a rails model where two attributes need to share the same association with another model?

邮差的信 提交于 2019-12-24 09:27:39

问题


I'm working on a Rails app that is a compendium of quotes that artists have said about other artists, and I'm having trouble figuring out how the relationships in my data model should work.

Take this quote as an example. Here is something David Bowie said about Lou Reed:

"He was a master"

My attributes for a Quote model in this example would be something like this:

  • Excerpt => "He was a Master"
  • Speaker => "David Bowie"
  • Subject => "Lou Reed"

But what if Lou Reed said something about David Bowie, such as this:

  • Excerpt => "He was my best friend in the 70s"
  • Speaker => "Lou Reed"
  • Subject => "David Bowie"

An artist could be both a speaker or a subject of a Quote, but never of the same quote. I'd also like users to be able to create new Artists and then have those appear as options to associate with a quote (via a dropdown or search, etc.) when they are creating a Quote.

It seems that I can't structure it so that the Quote has many Artists through Speakers, because it could also have many Artists through Subjects.

Is this the idiomatic Rails way to structure this:

Quote
has_one :speaker
has_one :subject

Speaker
has_many :artists
belongs_to_many :quotes

Subject
has_many :artists
belongs_to_many :quotes

Artist
belongs_to_many :speakers
belongs_to_many :subjects

回答1:


I believe you want Quote to look something like:

class Quote < ActiveRecord::Base
  belongs_to :speaker, class_name: "Artist"
  belongs_to :subject, class_name: "Artist"
  belongs_to :user

  validates :speaker, uniqueness: {scope: :subject}
  validates :subject, uniqueness: {scope: :speaker}

  ...

end

This assumes:

  • A User (probably current_user or something like that) is creating the quote and that the quote belongs to that user.
  • An Artist is a distinct notion from User. That is, an Artist is not necessarily a User (a Quote may have David Bowie as a subject, but David Bowie may not be a User of your system (especially given that he has passed away as has Lou Reed)).

This way, both speaker and subject are specified as being of the class Artist.

The uniqueness validations ensure that a speaker is never a subject and visa-versa. (You may need to twiddle with the validates statement as I did not test.)

In your Artist model, you probably want to do something like:

class Artist < ActiveRecord::Base
  has_many :spoken_quotes, class_name: "Quote", foreign_key: :speaker_id
  has_many :referencing_quotes, class_name: "Quote", foreign_key: :subject_id

  ...

end

That way, you could do something like:

Artist.find_by(name: 'David Bowie').spoken_quotes

And get all the Quotes for which David Bowie is the speaker.

And, in your User model, you probably want something like:

class User < ActiveRecord::Base
  has_many: :quotes

  ...

end

So that you can do something like:

current_user.quotes

And get all the quotes the current_user has created.




回答2:


let's say you have the following schema:

user - id
quote - author_id, subject_id

you can make the relations like so, making use of the options available to these association methods:

# User
has_many :sent_quotes, class_name: "Quote", foreign_key: :author_id
has_many :received_quotes, class_name: "Quote", foreign_key: :subject_id
has_many :quote_receivers, through: :sent_quotes, source: :subject
has_many :quote_senders, through: :received_quotes, source: :authors

# Quote
belongs_to :author, class_name: "User"
belongs_to :subject, class_name: "User"


来源:https://stackoverflow.com/questions/48214369/how-do-you-structure-a-rails-model-where-two-attributes-need-to-share-the-same-a

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!