I have 3 models user, micropost, like
I followed Ruby on Rails Tutorial by Michael Hartl
https://rails-4-0.railstutorial.org/book
Now I am adding new features and was trying to add a like button and after some changes now my micropost delete button doesn't work.
#micropost_controller
def destroy
Micropost.find(params[:id]).destroy
redirect_to root_url
end
#_micropost.html.erb
<%= link_to "delete", micropost, method: :delete,
data: { confirm: "You sure?" },
title: micropost.content %>
Even though i am calling Micropost.find Active Record searches in likes table and gives the error as
ActiveRecord::StatementInvalid in MicropostsController#destroy
SQLite3::SQLException: no such column: likes.micropost_id: SELECT "likes".* FROM "likes" WHERE "likes"."micropost_id" = ?
Similar thing happens when i try to delete a user. So basically whichever destroy i am trying to call it is redirected to like model
PS: the problem started after i executed
ActiveRecord::Base.connection.execute("BEGIN TRANSACTION; END;")
as i was getting BusyException: database is locked: commit transaction
models
#like.rb
class Like < ApplicationRecord
belongs_to :microposts, optional: true
belongs_to :users, optional: true
validates :src_user_id, presence: true
validates :des_user_id, presence: true
validates :post_id, presence: true
end
#micropost.rb
class Micropost < ActiveRecord::Base
belongs_to :user
default_scope -> { order('created_at DESC') }
validates :content, presence: true, length: { maximum: 140 }
validates :user_id, presence: true
has_many :likes, dependent: :destroy
# Returns microposts from the users being followed by the given user.
def self.from_users_followed_by(user)
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
where("user_id IN (#{followed_user_ids}) OR user_id = :user_id",
user_id: user.id)
end
def self.search(search)
where("content LIKE ?", "%#{search}%")
end
end
#user.rb
class User < ActiveRecord::Base
has_many :microposts, dependent: :destroy
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
has_many :likes, foreign_key: "src_user_id", dependent: :destroy
has_many :liked_by_users, through: :likes, source: :likes
scope :starts_with, -> (name) { where("name like ?", "#{Example User}%")}
before_save { self.email = email.downcase }
before_create :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, unless: Proc.new { |user| user.password.nil? }
validates :password_confirmation, presence: true, unless: Proc.new { |user| user.password.nil? }
validates :birthday, presence: true
validate :email_verification, on: :update
end
class User
has_many :microposts, dependent: :destroy
# You never have to specify foreign key for indirect assocations
has_many :likes, through: :microposts
end
class Micropost
belongs_to :user
has_many :likes, dependent: :destroy, foreign_key: "post_id"
end
class Like
# You must specify the foreign key if it cannot be derived from the name of the association
# `belongs_to :micropost` will use micropost_id
belongs_to :micropost, optional: true, foreign_key: "post_id"
belongs_to :user # you have to add a `likes.user_id` column
end
Although I would really question if you want to use the same table/model for user likes and micropost likes. It really just makes things a lot more complicated and you end up with a table with a large number of null values. If this is part of the book I really wonder what the author was thinking as its a really bad design.
You can just set it up as:
# app/models/users/like.rb
# table name is users_likes
module Users
class Like
belongs_to :src_user, class_name: 'User', optional: false
belongs_to :des_user, class_name: 'User', optional: false
end
end
class User
has_many :likes_as_src,
class_name: 'Users::Like',
foreign_key: :src_user
has_many :likes_as_des,
class_name: 'Users::Like',
foreign_key: :src_user
end
# app/models/users/like.rb
# table name is microposts_likes
module Microposts
class Like
belongs_to :user, optional: false
belongs_to :micropost, optional: false, foreign_key: 'post_id'
end
end
class User
has_many :micropost_likes, class_name: 'Microposts::Like'
has_many :liked_microposts, through: :micropost_likes, source: :micropost
end
That gives you two simple join tables with non-nullable foreign keys and good indices. It also makes the validations very straight forward.
Correct associations would be
#user.rb
has_many :microposts, dependent: :destroy
has_many :likes, through: :microposts, foreign_key: "post_id"
#micropost.rb
belongs_to :user
has_many :likes, dependent: :destroy, foreign_key: "post_id"
#like.rb
belongs_to :micropost, optional: true
After this, it works fine
I want to define a method (complete) that returns true if a user has watched all lessons of a course. Right now the method only returns false even if all the lessons are viewed.
User model:
has_many :enrolments, dependent: :destroy
has_many :courses, through: :enrolments
has_many :views
has_many :lessons, through: :views
def enrol(course)
courses << course
end
def enrolled?(course)
courses.include?(course)
end
def view(lesson)
lessons << lesson
end
def viewed?(lesson)
lessons.include?(lesson)
end
def complete(course)
if self.viewed?(course.lessons.all)
return true
else
return false
end
end
Lesson Model:
belongs_to :course
has_one_attached :file
default_scope -> { order(created_at: :desc) }
has_many :views
has_many :users, through: :views
Course Model:
belongs_to :company
has_many :lessons, dependent: :destroy
default_scope -> { order(created_at: :desc) }
has_many :enrolments, dependent: :destroy
has_many :users, through: :enrolments
View Model:
belongs_to :user
belongs_to :lesson
Your viewed? checks only for a single lesson, but you're passing a relation there.
For this case it's sufficient to compare arrays with lessons ids:
def complete(course)
lessons.where(course: course).ids.sort == course.lessons.ids.sort
end
class Task < ActiveRecord::Base
belongs_to :project, :inverse_of => :tasks
accepts_nested_attributes_for :project
end
class Project < ActiveRecord::Base
belongs_to :workspace, :inverse_of => :projects
has_many :tasks, :inverse_of => :project,
dependent: :destroy,
autosave: true
accepts_nested_attributes_for :workspace
end
class Workspace < ActiveRecord::Base
has_many :projects, inverse_of: :workspace,
dependent: :destroy,
autosave: true
has_many :tasks, through: :projects
end
I am able to save the child object 'project' from 'workspace'. But I can't save the grandchild object 'task' from 'workspace'. What's wrong? How can I do that without additional '.save' calls? I don't want to call #p.save
####child####
#w = Workspace.first
#p = #w.projects.new(name: "text")
#w.save
#p.new_record? #false, which is ok
###grandchild###
#w = Workspace.first
#p = #w.projects.first
#t = #p.tasks.new(name: "text")
#w.save
#t.new_record? #true, but should've been saved
Given the following models:
Business
class Business < ActiveRecord::Base
## OWNERS / EMPLOYEES
has_many :business_users, as: :business, dependent: :destroy
has_many :users, through: :business_users
accepts_nested_attributes_for :business_users, allow_destroy: true, reject_if: lambda { |a| a[:user_id].blank? || a[:role].blank? }
end
BusinessUser
class BusinessUser < ActiveRecord::Base
belongs_to :business, polymorphic: true
belongs_to :user
enum role: {:owner => 0, :employee => 1, :admin => 2}
validates_presence_of :user
validates :user, uniqueness: { scope: :business, message: "Each user can only have one role" }
end
User
class User < ActiveRecord::Base
has_many :business_users
has_many :businesses, through: :business_users, source_type: Business, source: :business
end
How can i make sure that each business has at least one business_user with role :owner?
I created concern in which placed association.
I need to make polymorphic association status.
has_one :status, class_name: 'VideoStatus', inverse_of: :video, dependent: :destroy
belongs_to :video, inverse_of: :status
I can't make that Association.How to make it?
require 'active_support/concern'
module EncodeStatuses
extend ActiveSupport::Concern
FORMATS = %w[mp4]
HIGH_VERSION = 'high'
MEDIUM_VERSION = 'medium'
LOW_VERSION = 'low'
VERSIONS = [HIGH_VERSION, MEDIUM_VERSION, LOW_VERSION]
included do
has_one :status, class_name: 'VideoStatus', inverse_of: :video, dependent: :destroy
accepts_nested_attributes_for :status, update_only: true
delegate :success?, :failure?, :waiting?, :encoding?, to: :status
end
end
models/video.rb
class Video < ActiveRecord::Base
include EncodeStatuses
...
end
models/post.rb
class Post < ActiveRecord::Base
include EncodeStatuses
...
end
models/video_status.rb
class VideoStatus < ActiveRecord::Base
STATUS_WAITING = 'waiting'
STATUS_ENCODING = 'encoding'
STATUS_SUCCESS = 'success'
STATUS_FAILURE = 'failure'
STATUSES = [STATUS_WAITING, STATUS_ENCODING, STATUS_SUCCESS, STATUS_FAILURE]
belongs_to :video, inverse_of: :status
belongs_to :post, inverse_of: :status
...
end
has_one :status, as: :video_status, dependent: :destroy
class VideoStatus < ActiveRecord::Base
belongs_to :video_status, polymorphic: true
end