I have the following models in Rails 4 with a simple has_many :through association:
class Plan < ActiveRecord::Base
belongs_to :project
belongs_to :item
quantity: decimal
end
class Project < ActiveRecord::Base
has_many :plans
has_many :items, through: :plans
end
class Item < ActiveRecord::Base
has_many :plans
has_many :projects, through: :plans
belongs_to :unit
end
I have 6 plans:
2.1.1 :002 > Plan.all.count
(0.1ms) SELECT COUNT(*) FROM "plans"
=> 6
I can't acces to quantity attribute:
2.1.1 :014 > Plan.where(item_id: 1, project_id: 2).count
(0.1ms) SELECT COUNT(*) FROM "plans" WHERE "plans"."item_id" = 1 AND "plans"."project_id" = 2
=> 1
2.1.1 :015 > Plan.where(item_id: 1, project_id: 2).quantity
NoMethodError: Plan Load (0.1ms) SELECT "plans".* FROM "plans" WHERE "plans"."item_id" = 1 AND "plans"."project_id" = 2
undefined method `quantity' for #<ActiveRecord::Relation::ActiveRecord_Relation_Plan:0x000000020a8d48>
When a plan contains an item and add same item one more, then have to increase quantity.
The plan controller:
def create
#plan = Plan.new(plan_params)
#plan.project_id = #project.id
if #project.plans.where(:item_id => #plan.item_id).blank?
#plan.save
redirect_to project_plans_url
else
**?????**
redirect_to project_plans_url
end
end
private
def set_project
#project = Project.find(params[:project_id])
end
def plan_params
params.require(:plan).permit(:item_id, :quantity)
end
How can I manage the controller else branch?
You need something like this:
#plan.project_id = #project.id
new_plan = #project.plans.where(item_id: #plan.item_id).first_or_initialize
new_plan.quantity += 1
new_plan.save
and in that case you don't need else at all.
Related
I prefer use includes to connect my three tables. I have three model that I need to connect such as Register, Student, and Schedule. Here is my model Association
class Student < ApplicationRecord
belongs_to :register
end
class Register < ApplicationRecord
has_one :student
belongs_to :schedule
end
class Schedule < ApplicationRecord
belongs_to :course
belongs_to :teacher
has_many :days, :dependent => :destroy, :class_name => 'ScheduleDay'
has_many :registers
end
Here is my controller
def index
#students = Student.includes([register: :schedule])
#students = #students.order 'students.created_at DESC'
if params[:status_id] && params[:status_id].to_i > 0
#students = #students.where 'students.status_id = ?', params[:status_id]
end
if params[:id] && params[:id].to_i > 0
#students = #students.where 'cast(students.id as varchar) like (?)', "%#{params[:id]}%"
end
if params[:full_name] && params[:full_name].to_s.length > 0
#students = #students.where 'lower(registers.name_in_indonesian || registers.name_in_chinese) like lower(?)', "%#{params[:full_name]}%"
end
if params[:course_id] && params[:course_id].to_i > 0
#students = #students.where 'schedules.course_id = ?', params[:course_id]
end
#students = #students.paginate(page: params[:page], per_page: 30)
end
I got following error :
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "schedules"
LINE 1: SELECT "students".* FROM "students" WHERE (schedules.course_...
I tried to debug by using rails console but end up with (Object doesn't support #inspect)
You need to join those tables if you want to query them, includes is not enough:
#students = Student.joins(register: :schedule)
or add a references call:
#students = Student.includes(register: :schedule).references(register: :schedule)
includes will reduce the number of database queries to access the included tables but it won't JOIN the tables.
I think you are having some unmigrated migrations. Please run
rake db:migrate
After running above command restart rails console and then try the same thing. You will not be getting this error.
I have User and Review models. A review can have an author and a subject, both pointing to a User:
class Review < ApplicationRecord
belongs_to :subject, class_name: 'User', optional: true
belongs_to :author, class_name: 'User', optional: true
end
class CreateReviews < ActiveRecord::Migration[5.0]
def change
create_table :reviews do |t|
t.references :subject
t.references :author
end
end
end
This works fine and now I can assign two separate User objects to the Review object to represent who wrote the review against whom.
The user though, doesn't "know" how many reviews he's associated with either as a subject or the author. I added has_and_belongs_to_many :users on reviews and vice-versa, and though doable, isn't exactly what I want.
How do I set up the associations to be able to do the following:
review.author = some_other_user
review.subject = user2
another_review.author = some_other_user
another_review.subject = user2
user2.a_subject_in.count
#=> 2
user2.a_subject_in
#=> [#<Review>, #<Review>]
some_other_user.an_author_in.count
#=> 2
In other words, how do I see how many times a User has been saved as an author or subject for a model with belongs_to?
IF you want to use has_many association on users side, you need to define two separate has_many relations like
class User < ApplicationRecord
has_many :reviews, foreign_key: :author_id
has_many :subject_reviews, class_name: 'Review', foreign_key: :subject_id
end
Now with this you can simply use
irb(main):033:0> s.reviews
Review Load (0.2ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."author_id" = ? [["author_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Review id: 1, comment: "random", subject_id: 2, author_id: 1, created_at: "2016-07-12 01:16:23", updated_at: "2016-07-12 01:16:23">]>
irb(main):034:0> s.subject_reviews
Review Load (0.2ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."subject_id" = ? [["subject_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy []>
Comment: subject_reviews is not a good name :), change it to your requirements.
I think you're looking for this query:
class User
def referenced_in
# this fetches you all the reviews that a user was referenced
Review.where("reviews.author_id = :user_id OR reviews.subject_id = :user_id", user_id: id).distinct
end
end
User.first.referenced_in #should give you all records a user was referenced
I'm trying to eager load results with 5 tables:
class Meeting < ActiveRecord::Base
has_many :bookings
end
class Booking < ActiveRecord::Base
belongs_to :user
belongs_to :meeting
belongs_to :role
belongs_to :group
end
class Group < ActiveRecord::Base
has_many :group_memberships
has_many :users, through: :group_memberships
end
class GroupMembership < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
class User < ActiveRecord::Base
has_many :group_memberships
has_many :groups, through: :group_memberships
end
Inside MeetingController.rb:
def index
#meetings = Meeting.where(year: #year)
end
I'm trying to use includes(:bookings) but according to bullet-gem the eager loading is not working. And inside my view I'm calling this line:
#meetings.each do |meeting|
meeting.bookings.find_by(group: #group, role: #role)
end
I'm getting multiple 52 lines of this code which clearly means that I'm not eager loading any data:
Booking Load (0.1ms) SELECT "bookings".* FROM "bookings" WHERE "bookings"."meeting_id" = ? AND "bookings"."user_id" = 8 AND "bookings"."group_id" = 2 LIMIT 1 [["meeting_id", 207]]
Rendered shared/_book.html.haml (1.0ms)
Booking Load (0.1ms) SELECT "bookings".* FROM "bookings" WHERE "bookings"."meeting_id" = ? AND "bookings"."group_id" = 2 AND "bookings"."role_id" = 4 LIMIT 1 [["meeting_id", 208]]
Booking Load (0.0ms) SELECT "bookings".* FROM "bookings" WHERE "bookings"."meeting_id" = ? AND "bookings"."user_id" = 8 AND "bookings"."group_id" = 2 LIMIT 1 [["meeting_id", 208]]
Rendered shared/_book.html.haml (1.0ms)
Booking Load (0.1ms) SELECT "bookings".* FROM "bookings" WHERE "bookings"."meeting_id" = ? AND "bookings"."group_id" = 2 AND "bookings"."role_id" = 4 LIMIT 1 [["meeting_id", 209]]
bullet.log:
2014-03-29 15:30:48[WARN] user: karlingen
localhost:3000http://localhost:3000/meetings/host
Unused Eager Loading detected
Meeting => [:bookings]
Remove from your finder: :include => [:bookings]
Any ideas what I should be doing?
Try the following:
#meetings = Meeting.includes(:bookings).where(year: #year)
Also, how are you passing in the #year?
Meeting.includes(:bookings).where(year: #year).references(:bookings)
will work if you are using rails 4.
When I query directly against my database I get the expected result, but not in rails. I'm guessing it has to do with my associations and something bad I said about Ruby 3 years ago.
Postgres SQL Query:
SELECT users.email, members.software, members.files
FROM users INNER JOIN members
ON members.user_id = users.id
WHERE members.region_id=2
Result: "dan#gmail.com";t;t "dan#test.com";t;t
BUT from rails c:
> ←[1m←[36mUser Load (1.0ms)←[0m ←[1mSELECT users.email,
> members.software, members.files FROM "users" INNER JOIN "members" ON
> "members"."user_id" = "users"."id" WHERE "members"."region_id" =
> 2←[0m> => #<ActiveRecord::Relation [#<User id: nil, email: "dan#gmail.com">, #<User id: nil, email: "dan#test.com">]>
That snippet was the resulting query from pasting in what I have tried to create in my controller and hard coding the region id:
User.joins(:members).select("users.email, members.software, members.files").where(members: {region_id: params[:id]})
These are my models:
class User < ActiveRecord::Base
has_many :members
has_many :regions, :through => :members
end
class Region < ActiveRecord::Base
has_many :members
has_many :users, :through => :members
end
class Member < ActiveRecord::Base
belongs_to :user
belongs_to :region
end
Is it the way I have associated my models or something else that I am missing?
Thanks!
What you are getting is active_relations object.
you can access the attributes like this
users = User.joins(:members).select("users.email, members.software as software, members.files as files").where(members: {region_id: params[:id]})
users.each do |u|
p u.email
p u.software
p u.files
end
Class Sale
belongs_to :product, :accessible => true
belongs_to :brand, :accessible => true
end
Class Product
has_many :sales
belongs_to :brand
end
Class Brands
has_many :products
has_many :sales
end
How do i get the brands that have the most product sales?
If you want to stay with activerecord you can use Calculations but it will take 2 queries to accomplish it:
>> brands = Sale.count(:id, :include => :brand, :group=> 'sales.brand_id', :limit => 5)
SQL (0.7ms) SELECT count(DISTINCT `sales`.id) AS count_id, sales.brand_id AS sales_brand_id FROM `sales` LEFT OUTER JOIN `brands` ON `brands`.id = `sales`.brand_id GROUP BY sales.brand_id LIMIT 5
=> #<OrderedHash {967032312=>3, 1392881137=>1}>
>> brands_with_most_sales = Brand.find(brands.keys)
Brand Load (0.5ms) SELECT * FROM `brands` WHERE (`brands`.`id` = 967032312)
=> [#<Brand id: 967032312, title: "brand-x", created_at: "2009-11-19 02:46:48", updated_at: "2009-11-19 02:46:48">]
Otherwise you might want to write you own query using find_by_SQL