can't delete object due to foreign key constraint - ruby-on-rails

I'm trying to do a simple user.destroy but running into the following error:
ERROR: update or delete on table "users" violates foreign key constraint "fk_rails_5373344100" on table "identities"
DETAIL: Key (id)=(2) is still referenced from table "identities".
Here is my migration for Identities
class CreateIdentities < ActiveRecord::Migration
def change
create_table :identities do |t|
t.references :user, index: true, foreign_key: true
t.string :provider
t.string :uid
t.timestamps null: false
end
end
end
Here is my user and identity model:
class Identity < ActiveRecord::Base
belongs_to :user
validates_presence_of :uid, :provider
validates_uniqueness_of :uid, :scope => :provider
def self.find_for_oauth(auth)
find_or_create_by(uid: auth.uid, provider: auth.provider)
end
end
and user:
class User < ActiveRecord::Base
TEMP_EMAIL_PREFIX = 'ricky#writeit.com'
TEMP_EMAIL_REGEX = /\ricky#writeit.com/
# Include default devise modules. Others available are:
# :lockable, :timeoutable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
validates_format_of :email, :without => TEMP_EMAIL_REGEX, on: :update
...
end
I'm new to foreign keys and references, so I'm not sure at all how to fix this.

You would need to remove the Identity that references the user first. Then you can delete the user.. By default the foreign key is doing a restrict so you cannot delete the user if anything references to it.
if you would like use Rails to handle destroying the identity you can do
class User < ActiveRecord::Base
has_many :identities, dependent: :destroy
......
end
Which would cause Rails to destroy all the dependent records.
But as you are using Foreign keys, you can adjust your migration to set cascade deletes
add_foreign_key :identities, :users, on_delete: :cascade
Assuming rails 4.2 which has native support

An easy solution is to simply cascade-delete the records in the associated table, which can be done through active record, like so:
user.rb
class User < ActiveRecord::Base
has_many :identities, dependent: :destroy
# rest of user class
end
Check out the documentation pertaining to has_many for more info.

Related

Rails poll users with different vote weight for each user

I am creating a poll app. I am modifying this https://www.sitepoint.com/polling-users-rails/ to my needs.
Users answer polls and results are shown.
polls
t.string :question
t.text :description
t.references :division, foreign_key: true
t.date :open_date
t.date :close_date
vote_options
t.string :title
t.references :poll, foreign_key: true
votes
t.references :user, foreign_key: true
t.references :vote_option, foreign_key: true
users
t.string :email
t.decimal :vote_weight
user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :votes, dependent: :destroy
has_many :vote_options, through: :votes
def voted_for?(poll)
vote_options.any? {|v| v.poll == poll }
end
end
vote_option.rb
class VoteOption < ApplicationRecord
belongs_to :poll
validates :question, presence: true
has_many :users,
has_many :votes, dependent: :destroy
def get_vote_count
VoteOption.joins(:votes).joins(:users).where(id: self.id).sum(:vote_weight)
end
end
vote.rb
class Vote < ApplicationRecord
belongs_to :user
belongs_to :vote_option
end
poll.helper
def visualize_votes_for(option)
content_tag :div, class: 'progress' do
content_tag :div, class: 'progress-bar',
style: "width: #{option.poll.normalized_votes_for(option)}%" do
"#{option.votes.count}"
end
visualize_votes_for shows total votes for each option. At the moment it considers 1 for each value and counts the total for each option.
I would like instead to be able to set a vote_weight for each user so that instead of 1 will be counted the value specified in vote_weight column in users table.
I have tried:
"#{sum(option.votes.user.vote_weight)}"
but it returns:
undefined method `user' for #<ActiveRecord::Associations::CollectionProxy []>
What am I doing wrong?
option.votes will return an active record collection of votes. Note that it will be a collection, not a single object. So, invoking method user on a collection will not work as a vote belongs to a user. So user method can be invoked only on an instance of vote object, not on collection.
You can make a method get_vote_count in VoteOption Model
def get_vote_count
Vote.joins(:vote_option).joins(:user).where("vote_options.id = #{self.id}").sum(:vote_weight)` # Adjust singularity/plurality of objects as per the requirement
end
And use this method in view dierctly on the option object like option.get_vote_count.

How do I make my task assignment associations?

I have a User model, a TodoList model, which has many todoItems. My models are :
User Model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
has_many :todo_lists
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
TodoList Model
class TodoList < ActiveRecord::Base
has_many :todo_items
belongs_to :user
end
ToItem Model
class TodoItem < ActiveRecord::Base
include AASM
belongs_to :todo_list
def completed?
!completed_at.blank?
end
#belongs_to :user
#belongs_to :friend, class_name: 'User', foreign_key: 'friend_id'
aasm :column => 'state', :whiny_transitions => false do
state :not_assigned, :initial => true
state :assigned
state :taskCompleted
end
I am trying to modify my models in such that any user can request to be assigned a taskItem and the user whom the task belongs to can accept or deny the requests. Once a an assignment request is approved, I want the task to be also associated to the user assigned to it.
How do I go about that with my model associations and relationships ? Thanks in advance for the help .
You could use an assignments association table, in a many-to-many relationship between User and TodoItem. Your association table would have an additional boolean attribute, indicating whether the item owner has accepted the request. Something like:
class TodoItem < ActiveRecord::Base
...
has_many :users, through: :assignments
...
end
For User:
class User < ActiveRecord::Base
...
has_many :todo_items, through: :assignments
...
end
And finally the association table:
class Assignment < ActiveRecord::Base
belongs_to :user
belongs_to :todo_item
end
Your migration to create the association table would be something like this:
class CreateAssignments < ActiveRecord::Migration
def change
create_table :assignments do |t|
t.belongs_to :user, index: true
t.belongs_to :todo_item, index: true
t.boolean :request_accepted, default: false, null: false
t.timestamps null: false
end
end
end

Private fields for multiple models, how to make on RoR?

I have 4 models with complex relations. 3 of them should have descriptions, that should be enable only for user who's create. In other words every user has his own description for Group (for example), or for Post, o something else. Let's talk about only one model, because others are very same. What I have:
user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, omniauth_providers: [:vkontakte]
has_and_belongs_to_many :groups
has_many :descriptions
end
group.rb
class Group < ActiveRecord::Base
has_and_belongs_to_many :users
has_many :descriptions, :as => :describable
accepts_nested_attributes_for :descriptions
end
description.rb
class Description < ActiveRecord::Base
belongs_to :user
belongs_to :describable, :polymorphic => true
end
table for descriptions
create_table "descriptions", force: :cascade do |t|
t.integer "user_id" -- belongs_to
t.string "content"
t.integer "describable_id"
t.string "describable_type"
end
How to display the description for group that belongs to current_user (I use devise)? How to build an update form with nested description?
I try to do it, but it's not work. I've ask question about part of problem here.
Why do you have an extra model called description?
Although it's not a problem in itself, you really don't need to have a model just for description.
--
Profile
Instead, you may wish to put the details into a profile model, or simply in the user model (there's nothing wrong with adding extra attributes to a Devise model).
We use a profile model, which gives us the ability to add as many "extra" fields as we want to the user model:
You can set it up like this:
#app/models/user.rb
class User < ActiveRecord::Base
has_one :profile
accepts_nested_attributes_for :profile
before_create :build_profile
delegate :description, :name, to: :profile, prefix: false #-> #user.description
end
#app/models/profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
end
This will allow you to create a single profile per user, have that profile built when the user is created, and then change as many options inside the profile as you wish.

Rails associating multiple :has_many

I'm new to Rails and trying to understand associating entities.
I have three entities right now: Users, Companies, and Productlines.
company.rb:
class Company < ActiveRecord::Base
has_many :users
has_many :productlines
end
user.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :company
end
productline.rb:
class Productline < ActiveRecord::Base
belongs_to :company
end
I've done the migration scrips to associate them, however when I click into a Company on Rails Admin, I'm getting this:
SQLite3::SQLException: no such column: productlines.company_id: SELECT "productlines".* FROM "productlines" WHERE "productlines"."company_id" = ?
Extracted source (around line #91):
#
def prepare sql
stmt = SQLite3::Statement.new( self, sql )
return stmt unless block_given?
begin
Edit: Here is my migration:
class AddProductlineIdToCompanies < ActiveRecord::Migration
def change
add_column :companies, :productline_id, :integer
add_index :companies, :productline_id
end
end
You should add the company_id to productlines and not product_line_id to companies...
class AddCompanyIdToProductlines < ActiveRecord::Migration
def change
add_column :productlines, :company_id, :integer
end
end

SQLite3::SQLException while trying to set foreign keys?

I have three models: User, Micropost, and Comment. I'm trying to set foreign keys as follows:
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.text :content
t.timestamps
end
add_index :comments, :micropost_id, :user_id
end
end
But I get this error:
An error has occurred, this and all later migrations canceled:
SQLite3::SQLException: near "user_id": syntax error: CREATE user_id
INDEX "index_comments_on_micropost_id" ON "comments" ("micropost_id")
I understand that Rails insert foreign keys based on belongs_to and has_many declarations in the models. But I have everything set:
comment.rb:
class Comment < ActiveRecord::Base
belongs_to :micropost
belongs_to :user
end
micropost.rb:
class Micropost < ActiveRecord::Base
attr_accessible :title, :content
belongs_to :user
has_many :comments
end
user.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
has_many :microposts
has_many :comments
end
Any suggestions to fix this?
If you want to create an index on 2 columns, the syntax is add_index table_name, [column1_name, column2_name], options. You also need to define the columns in the table (ActiveRecord does not add them automatically when you add belongs_to in the model class). So your migration should be
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.text :content
t.integer :micropost_id
t.integer :user_id
t.timestamps
end
add_index :comments, [:micropost_id, :user_id]
end
end

Resources