ActiveRecord Validations for Models with has_many, belongs_to associations and STI
I have four models:
- User
- Award
- Badge
- GameWeek
The associations are as follows:
- User has many awards.
- Award belongs to user.
- Badge has many awards.
- Award belongs to badge.
- User has many game_weeks.
- GameWeek belongs to user.
- GameWeek has many awards.
- Award belongs to game_week.
Thus, user_id, badge_id and game_week_id are foreign keys in awards table.
Badge implements an STI model. Let's just say it has the following subclasses: BadgeA and BadgeB.
Some rules to note:
The game_we开发者_如何学Pythonek_id fk can be nil for BadgeA, but can't be nil for BadgeB.
Here are my questions:
- For BadgeA, how do I write a validation that it can only be awarded one time? That is, the user can't have more than one -- ever.
- For BadgeB, how do I write a validation that it can only be awarded one time per game week?
Data model:
In my comprehension, here is your data model (click to enlarge):
Data model http://yuml.me/6afcad62
Migration:
The migration will let you meet your second requirement, at the migration level:
class CreateAwards < ActiveRecord::Migration
def self.up
create_table :awards do |t|
# custom attributes here
t.string :name
t.text :description
t.references :user, :null => false
t.references :game_week#, :null => false
t.references :badge, :null => false
t.timestamps
end
# a user can be awarded no more than a badge per week
add_index :awards, [:user_id, :badge_id, :game_week_id], :unique => true
# a user can be awarded no more than a badge for ever
#add_index :awards, [:user_id, :badge_id], :unique => true
end
def self.down
drop_table :awards
end
end
Model:
The model will let you meet both your requirements, at the model level:
class Award < ActiveRecord::Base
validate_uniqueness_of :user, :badge,
:if => Proc.new { |award| award.badge === BadgeA }
validate_uniqueness_of :user, :badge, game_week,
:unless => Proc.new { |award| award.badge === BadgeA }
#validate_uniqueness_of :user, :badge, game_week,
# :if => Proc.new { |award| award.badge === BadgeB }
end
Note:
I didn't try these snippets, but I think that the idea is here :)
精彩评论