Reputation: 1019
I have two associated models in Rails. User, and Post.
Whilst adding a boolean value column, 'Avatar' to the posts table, I would like to make a true value unique to the user to which it belongs.
In other words users can only have one 'Avatar' post with a value of true. This is because these posts (which are pictures) can be used as a users Avatar.
The posts already belong to users, and users have many posts, so I would like to set the constraint that users only have one post with a value of true for Avatar. Also if a new Avatar is uploaded/selected, then for this action to cancel out and falsify any previous Avatar.
This is my migration file so far:
add_avatar_to_user_profiles.rb
class AddAvatarToUserProfiles < ActiveRecord::Migration
def change
add_column :posts, :avatar, :boolean, default: false
end
end
schema.rb
ActiveRecord::Schema.define(version: 20151018160617) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "comments", force: :cascade do |t|
t.text "comment"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
t.integer "user_id"
end
add_index "comments", ["post_id"], name: "index_comments_on_post_id", using: :btree
add_index "comments", ["user_id"], name: "index_comments_on_user_id", using: :btree
create_table "posts", force: :cascade do |t|
t.string "caption"
t.integer "likes"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
end
add_index "posts", ["user_id"], name: "index_posts_on_user_id", using: :btree
create_table "sessions", force: :cascade do |t|
t.string "session_id", null: false
t.text "data"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "sessions", ["session_id"], name: "index_sessions_on_session_id", unique: true, using: :btree
add_index "sessions", ["updated_at"], name: "index_sessions_on_updated_at", using: :btree
create_table "users", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.string "provider"
t.string "uid"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["provider"], name: "index_users_on_provider", using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
add_index "users", ["uid"], name: "index_users_on_uid", using: :btree
add_foreign_key "comments", "posts"
add_foreign_key "comments", "users"
add_foreign_key "posts", "users"
end
Hoping the above is enough to make sense, I have searched docs to no avail, am thinking the solution should be simple enough.
Upvotes: 2
Views: 898
Reputation: 34318
You can add a custom validator in your Post
model like this:
# post.rb
validate :unique_post_avatar
private
def unique_post_avatar
if self.user.posts.select { |p| p.avatar == true }.count > 1
errors.add(:avatar, "only one post avatar for this user can be true")
end
end
which is essentially selecting the user's posts where the avatar is true. If this count is greater than one, then it will throw validation error. I think this is what you are looking for :-)
Upvotes: 2