SQLite3::SQLException while trying to set foreign keys? - ruby-on-rails

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

Related

How do I write a simple if statement to check if the current user has already reviewed something?

In the view I have tried everything. Currently I have:
Leave a Review
<% if current_user.reviews.include?(#book) %>
<p>You've already reviewd this</p>
<% else %>
<div id="review-links">
<%= link_to "write a review", new_book_review_path(#book.id) %>
</div>
<% end %>
<% end %>
user.rb
class User < ApplicationRecord
mount_uploader :avatar, AvatarUploader
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :books, dependent: :destroy
has_many :reviews , dependent: :destroy
enum access_level: [:author, :reviewer]
has_and_belongs_to_many :claims, join_table: :books_users, association_foreign_key: :book_id, class_name: Book.to_s
def full_name
first_name + " " + last_name
end
end
book.rb
class Book < ApplicationRecord
mount_uploader :avatar, AvatarUploader
belongs_to :user
has_many :reviews
has_and_belongs_to_many :genres, dependent: :destroy
has_and_belongs_to_many :claims, join_table: :books_users, association_foreign_key: :user_id, class_name: User.to_s
end
review.rb
class Review < ApplicationRecord
belongs_to :user
belongs_to :book
end
schema.rb
create_table "reviews", force: :cascade do |t|
t.text "comment"
t.integer "rating"
t.integer "user_id"
t.integer "book_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["book_id"], name: "index_reviews_on_book_id"
t.index ["user_id"], name: "index_reviews_on_user_id"
end
nfortunately I still have the link showing to leave a review on the book's show page even though the reviewer has already written one for that book. What am I doing wrong?

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

can't delete object due to foreign key constraint

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.

Rails: How to set up Active Record Associations?

I am relatively new to rails and doing the simplest thing: Associate users and posts. I read this, but what more than this do I need to do to make it work (or is this the only thing)?:
class User < ActiveRecord::Base
has_many :posts, :dependent => :destroy
end
class Post < ActiveRecord::Base
belongs_to :user
end
Update:
I can't make it work. When I make a post with a signed in user, I get false when I do #user.posts.any? in the console. My code:
post.rb
class Post < ActiveRecord::Base
attr_accessible :title, :user_id
belongs_to :user
before_create :default_values
user.rb (I use devise)
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me
has_many :posts, dependent: :destroy
end
20130320162700_create_posts.rb
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :title
t.integer :user_id
t.timestamps
end
end
end
You should make sure to include User's id in the migration that creates the posts table.
In your migration file (in the db/migrate folder you will find a file named like 20130325105934_create_posts.rb)
Inside the file you will find the migration code. Along the other declared attributes add
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
......
t.integer :user_id
......
end
end
It should be enough to make things roll :-)
Inside your code then you can create a new user as
#user = User.new(:login => "my_user", .....)
and then add posts with one of these two ways (there are others two).
post = Post.new(:title => "something", :text => "more of something", :user_id = #user.id)
or
post = Post.new(:title => "something", :text => "more of something")
#user.posts << post

Resources