Barris
Barris

Reputation: 1019

Add constraint to table from Rails - ActiveRecord

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

Answers (1)

K M Rakibul Islam
K M Rakibul Islam

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

Related Questions