问题
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