Add constraint to table from Rails - ActiveRecord - ruby-on-rails

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.

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 :-)

Related

Rails: how to create a migration that add 'using: :btree' to foreign key

I have two models
father has_many sons
son belongs_to father
Normally, this piece of code
def change
add_reference :sons, :father, index: true
end
it will generate
add_index "sons", ["father_id"], name: "index_sons_on_father_id"
Now, I want it generate
add_index "sons", ["father_id"], name: "index_sons_on_father_id", using: :btree
How to write the migration?
you just check this
you can do look like this.
ActiveRecord::Schema.define(version: 20150321171548) do
create_table "authors", force: :cascade do |t|
t.string "name"
t.string "hashtags"
t.string "avatar"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "articles", force: :cascade do |t|
t.integer "author_id", limit: 4
t.string "title", limit: 255
t.string "bannerurl", limit: 255
t.string "thumbnailurl", limit: 255
t.text "content", limit: 65535
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "articles", ["author_id"], name: "index_articles_on_author_id", using: :btree
end

rails eager loading for one-to-many association

I have
class Article < ActiveRecord::Base
has_one :template
end
and
class Template < ActiveRecord::Base
has_many :articles
end
where Template model has got a room property. Now i'd like to build a list of all articles, where the articles template has a certain room value (say "bath").
I thought this is done by eager loading (resp: includes), but if i try
Article.includes(:template)
I get the error
SELECT "templates".* FROM "templates" WHERE "templates"."article_id" IN ('51', '52', '53', '54')
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column templates.article_id does not exist
LINE 1: SELECT "templates".* FROM "templates" WHERE "templates"."art...
What am I doing wrong?
EDIT
here's my schema.rb as asked
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160913122551) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "articles", force: :cascade do |t|
t.string "title"
t.text "details"
t.integer "value_eur"
t.integer "deposit_eur"
t.integer "location_id"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "picture"
t.float "rate_eur"
t.string "rate_interval"
t.integer "template_id"
t.integer "quality"
end
add_index "articles", ["location_id"], name: "index_articles_on_location_id", using: :btree
add_index "articles", ["template_id"], name: "index_articles_on_template_id", using: :btree
add_index "articles", ["user_id"], name: "index_articles_on_user_id", using: :btree
create_table "locations", force: :cascade do |t|
t.string "street_and_no"
t.string "postcode"
t.string "city"
t.string "country"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.float "latitude"
t.float "longitude"
end
add_index "locations", ["user_id"], name: "index_locations_on_user_id", using: :btree
create_table "templates", force: :cascade do |t|
t.string "title"
t.text "details_hint"
t.float "rate_eur"
t.string "rate_interval"
t.string "picture"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "room"
end
create_table "users", force: :cascade do |t|
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.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "role"
t.string "nickname"
t.string "firstname"
t.string "lastname"
t.string "phoneno"
t.boolean "showemail"
t.boolean "showphone"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["nickname"], name: "index_users_on_nickname", unique: true, using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
add_foreign_key "articles", "locations"
add_foreign_key "articles", "templates"
add_foreign_key "articles", "users"
add_foreign_key "locations", "users"
end
Your templates table does not have an article_id column according to the schema.rb you posted so you will need to create that reference.
Change
has_one :template
in the articles model to
belongs_to :template

ActiveAdmin Categories always goes to default setting in Rails App

Hi I´m learning to use ActiveAdmin by building an rails App.
I've a products and a category model which are related Product belongs to category.
In the Admin Panel I can create Categories and Products
When I´m creating new product and assigning it to a category I have created the product always ends up belonging to a category called "No Category"
this "No category" option is supposed to be default if no other category is assigned to the product.
I´m not sure why this is happening since I can choose from multiple categories that I've created.
this is my product model
class Product < ActiveRecord::Base
belongs_to :category
belongs_to :page
validates :title, :description, presence: true
validates :price_usd, :price_isl, numericality: {greater_than_or_equal_to: 0.01}
validates :title, uniqueness: true
has_attached_file :image, styles: { medium: "500x500#", thumb: "100x100#" }
validates_attachment_content_type :image, content_type: /\Aimage\/.*\z/
end
and this is my category.rb model
class Category < ActiveRecord::Base
has_many :products
end
And here is a copy of my Schema.rb
ActiveRecord::Schema.define(version: 20160826161114) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "active_admin_comments", force: :cascade do |t|
t.string "namespace"
t.text "body"
t.string "resource_id", null: false
t.string "resource_type", null: false
t.integer "author_id"
t.string "author_type"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "active_admin_comments", ["author_type", "author_id"], name: "index_active_admin_comments_on_author_type_and_author_id", using: :btree
add_index "active_admin_comments", ["namespace"], name: "index_active_admin_comments_on_namespace", using: :btree
add_index "active_admin_comments", ["resource_type", "resource_id"], name: "index_active_admin_comments_on_resource_type_and_resource_id", using: :btree
create_table "admin_users", force: :cascade do |t|
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.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "admin_users", ["email"], name: "index_admin_users_on_email", unique: true, using: :btree
add_index "admin_users", ["reset_password_token"], name: "index_admin_users_on_reset_password_token", unique: true, using: :btree
create_table "categories", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "pages", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "category_id", default: 5
end
add_index "pages", ["category_id"], name: "index_pages_on_category_id", using: :btree
create_table "products", force: :cascade do |t|
t.string "title"
t.string "image"
t.text "description"
t.decimal "price_usd"
t.decimal "price_isl"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.integer "category_id", default: 4
end
add_index "products", ["category_id"], name: "index_products_on_category_id", using: :btree
add_foreign_key "pages", "categories"
add_foreign_key "products", "categories"
end

NoMethodError (undefined method `val' for #<Arel::Nodes::BindParam:0x007fe5554268b8>): app/controllers/posts_controller.rb:10:in `new'

I am new to rails. Everything is working fine locally but after deploying on heroku its giving me the above error.
Here is the posts_controllers action:
def new
#post = current_user.posts.build
end
and here is the schema.rb file content:
ActiveRecord::Schema.define(version: 20160516214156) do
create_table "comments", force: :cascade do |t|
t.text "comment"
t.integer "post_id"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "comments", ["post_id"], name: "index_comments_on_post_id"
add_index "comments", ["user_id"], name: "index_comments_on_user_id"
create_table "posts", force: :cascade do |t|
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
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.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
I had the same error.
To solve the problem, I specified :foreign_key of has_many relation.
My case:
class Assay::Attempt::Answer < ActiveRecord::Base
belongs_to :assay_attempt, class_name: 'Assay::Attempt'
end
class Assay::Attempt < ActiveRecord::Base
has_many :answers, foreign_key: :assay_attempt_id # there wasn't foreign_key option
end
I came across this as well on Rails 4.2.0, in my case everything seemed to be ok, since I had the foreign_key specified, what fixed the issue was when the foreign_key and foreign_type were specified as a string, not as a symbol:
has_many :answers, foreign_key: :assay_attempt_id #this was not working
has_many :answers, foreign_key: 'assay_attempt_id' #this was ok

Heroku no implicit conversion of Symbol into Integer Error

I get the error with: heroku run rake db:migrate
Migrating to CreateResults (20150306174427)
== 20150306174427 CreateResults: migrating ====================================
-- create_table(:results)
-> 0.0479s
-- add_foreign_key(:results, :quantifieds, :users)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
no implicit conversion of Symbol into Integer/app/vendor/bundle/ruby/2.0.0/gems/activerecord-4.2.0.rc3/lib/active_record/connection_adapters/abstract/schema_statements.rb:707:in `[]'
/app/vendor/bundle/ruby/2.0.0/gems/activerecord-4.2.0.rc3/lib/active_record/connection_adapters/abstract/schema_statements.rb:707:in `add_foreign_key'
I can't understand it because run rake db:migrate works.
class CreateResults < ActiveRecord::Migration
def change
create_table :results do |t|
t.string :result_value
t.date :date_value
t.belongs_to :quantified, index: true
t.references :user, index: true
t.timestamps null: false
end
add_foreign_key :results, :quantifieds, :users
add_index :results, [:user_id, :created_at]
end
end
class CreateQuantifieds < ActiveRecord::Migration
def change
create_table :quantifieds do |t|
t.string :categories
t.string :metric
t.references :user, index: true
t.timestamps null: false
end
add_foreign_key :quantifieds, :users
add_index :quantifieds, [:user_id, :created_at]
end
end
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150306174427) do
create_table "days", force: true do |t|
t.integer "level_id"
t.integer "habit_id"
t.boolean "missed", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "goals", force: true do |t|
t.string "name"
t.date "deadline"
t.boolean "accomplished"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "goals", ["user_id", "created_at"], name: "index_goals_on_user_id_and_created_at"
add_index "goals", ["user_id"], name: "index_goals_on_user_id"
create_table "habits", force: true do |t|
t.datetime "left"
t.integer "level"
t.text "committed"
t.datetime "date_started"
t.string "trigger"
t.string "target"
t.string "reward"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "habits", ["user_id", "created_at"], name: "index_habits_on_user_id_and_created_at"
add_index "habits", ["user_id"], name: "index_habits_on_user_id"
create_table "levels", force: true do |t|
t.integer "user_id"
t.integer "habit_id"
t.integer "days_needed"
t.boolean "passed", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "quantifieds", force: true do |t|
t.string "categories"
t.string "metric"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "quantifieds", ["user_id", "created_at"], name: "index_quantifieds_on_user_id_and_created_at"
add_index "quantifieds", ["user_id"], name: "index_quantifieds_on_user_id"
create_table "relationships", force: true do |t|
t.integer "follower_id"
t.integer "followed_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "relationships", ["followed_id"], name: "index_relationships_on_followed_id"
add_index "relationships", ["follower_id", "followed_id"], name: "index_relationships_on_follower_id_and_followed_id", unique: true
add_index "relationships", ["follower_id"], name: "index_relationships_on_follower_id"
create_table "results", force: true do |t|
t.string "result_value"
t.date "date_value"
t.integer "quantified_id"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "results", ["quantified_id"], name: "index_results_on_quantified_id"
add_index "results", ["user_id", "created_at"], name: "index_results_on_user_id_and_created_at"
add_index "results", ["user_id"], name: "index_results_on_user_id"
create_table "taggings", force: true do |t|
t.integer "tag_id"
t.integer "taggable_id"
t.string "taggable_type"
t.integer "tagger_id"
t.string "tagger_type"
t.string "context", limit: 128
t.datetime "created_at"
end
add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true
add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context"
create_table "tags", force: true do |t|
t.string "name"
t.integer "taggings_count", default: 0
end
add_index "tags", ["name"], name: "index_tags_on_name", unique: true
create_table "users", force: true do |t|
t.string "name"
t.string "email"
t.text "missed_days"
t.text "missed_levels"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin", default: false
t.string "activation_digest"
t.boolean "activated", default: false
t.datetime "activated_at"
t.string "reset_digest"
t.datetime "reset_sent_at"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
create_table "valuations", force: true do |t|
t.string "name"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "valuations", ["user_id", "created_at"], name: "index_valuations_on_user_id_and_created_at"
add_index "valuations", ["user_id"], name: "index_valuations_on_user_id"
end
UPDATE
Making these changes worked:
$ heroku pg:reset DATABASE
$ heroku rake db:migrate
class CreateResults < ActiveRecord::Migration
def change
create_table :results do |t|
t.string :result_value
t.date :date_value
t.integer :quantified_id
t.timestamps null: false
end
end
end
Thanks so much for your help! And for another perplexing question check this out: https://stackoverflow.com/questions/28863999/how-to-automate-into-method
t.belongs_to
Is invalid, you want either references or change it to integer and add _id to the field name. Then on your index add square brackets round the second and third arguments.

Resources