I’m working on a three model relationship with one aspect that I’m not sure how to approach. Here’s the basic relationship:
class Taxonomy
has_many :terms
# attribute: `inclusive`, default => false
end
class Term
belongs_to :taxonomy
has_and_belongs_to_many :photos
end
class Photo
has_and_belongs_to_many :terms
end
This is pretty straightforward stuff except for one thing:
A Taxonomy can be either ‘Inclusive’ or ‘Exclusive’. Exclusive means the terms are mutually exclusive, Inclusive means they’re not.
Now, I can handle this on the client-side without a problem, but I am not quite sure how to set up a validation on Photos (or somewhere else) that says basically: “validate that no more than one term from an exclusive taxonomy is associated with this record.”
Bonus Question
Also, while it’s not essential, I’ve been thinking it would be nice to setup a join of some kind between Taxonomies and Photos, ie., I’d like an easy way to query for all the photos that have been classified with terms from a given taxonomy.
I think I can do this with something like Photo.where('term_id in ?', Taxonomy.find(1).terms.map(&:id)) but, obviously that’s not very elegant. I’m pretty sure has_many :through can’t work since terms are habtm to photos, so if anyone knows a more elegant way to setup that relationship I’d love to hear it.
Thanks!
Update, to further explain the taxonomy idea:
If a Taxonomy is exclusive ie. taxonomy.inclusive = false, then there can only be one term from that taxonomy attached to a given photo. Now, a Photo may have more than one taxonomy applied to it. For instance:
Taxonomy: Colors, Inclusive
Terms: Red (id 1), Blue (id 2), Green (id 3)
Taxonomy: Region, Exclusive
Terms: North America (id 4), South America (id 5)
So let’s say we have a photo with a lot of red and blue in it, so it gets those two terms, and it might be in South America, so it gets that term. But, a Photo can’t be of both North America and South America.
So in this case if we called: photo.terms.map &:id we would get [1,2,4]
On the client side I can basically do (pseudo code)
form_for photo
- Taxonomy.each do |tax|
- if tax.inclusive?
- tax.terms.each do |term|
- check_box term
- else
- tax.terms.each do |term|
- radio_button term
But on the model side, I would like to say, that a photo can have any of term_id 1,2,3, but only either/or 4 and 5.
Does that make sense?
For your validation problem, you can add a uniqueness validation on
Termmodel fortaxonomy_idfield which is only executed if the taxonomy is not inclusive, ie :You can also do this with a custom validation method :
Note that this is useless in this case as the first method give the same result but may be useful if you have more complex validation to do.
For the second part of your question, here is the way to make your query in ActiveRecord, let’s assume
taxonomyis the taxonomy for which you want the photos :UPDATE after your comment
I believe your validation should take place in the table which make the link between photos and terms. If you have following Rails convention, it’s actually the table
photos_terms.To add a validation on it, you will have to make it a first class citizen, ie: add an active record model for this table and an id field, let’s say you name it
PhotoTermLink.Here is the code for the validation you want :