Adding JOIN for associated tables [duplicate] - ruby-on-rails

This question already has an answer here:
Adding a JOIN between two tables
(1 answer)
Closed 9 years ago.
My Organization class looks something like this:
has_many Students
My Student class looks like this:
has_many Klasses
belongs_to Organization
My Klass class looks like this:
some field named : price
scope :top_expensive_classes, joins(:students).order('price DESC')
belongs_to Student
And my query looks like this:
#results = Klass.top_expensive_classes.where(organization_id: params[:id]).limit(RESULT_SET_COUNT)
Notice that it starts with Klass, so that's the problem because I am searching in the where class for organization_id but that is not in the Klass, it is in Student class , so somehow I should introduce a join somewhere to fix this but couldn't figure it out.

I think the real issue is your associations are likely incorrect.
A student has many classes
A class has many students
but what you have is
A student has many classes
A class belongs to a single student
This doesn't really make sense (at least in any situation I've ever seen a class and student interact). You should be creating a many-to-many relationship instead of a one-to-many relationship between Klass and Student.
class Student < ActiveRecord::Base
has_many :klasses, through: :student_klasses
has_many :student_klasses
end
class Klass < ActiveRecord::Base
has_many :students, through: :student_klasses
has_many :student_klasses
end
class StudentKlass < ActiveRecord::Base
belongs_to :student
belongs_to :klass
end
Once you have these correct associations in place, you need to call .joins on the :students association from the Klass class. You can do without the scope.
Klass.joins(:students).where("students.organization_id = ?", params[:id]).order('price DESC').limit(RESULT_SET_COUNT)
Read the guide on ActiveRecord Querying.
Here is the proof (using the exact model definitions above) that the ordering of the associations does not matter.
irb(main):001:0> s = Student.create(name: "Deefour")
SQL (3.6ms) INSERT INTO "students" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", Fri, 08 Mar 2013 01:33:32 UTC +00:00], ["name", "Deefour"], ["updated_at", Fri, 08 Mar 2013 01:33:32 UTC +00:00]]
=> #<Student id: 1, name: "Deefour", created_at: "2013-03-08 01:33:32", updated_at: "2013-03-08 01:33:32">
irb(main):002:0> kk = []
=> []
irb(main):003:0> kk << Klass.create(title: "Klass 1")
SQL (0.3ms) INSERT INTO "klasses" ("created_at", "title", "updated_at") VALUES (?, ?, ?) [["created_at", Fri, 08 Mar 2013 01:34:06 UTC +00:00], ["title", "Klass 1"], ["updated_at", Fri, 08 Mar 2013 01:34:06 UTC +00:00]]
=> [#<Klass id: 1, title: "Klass 1", created_at: "2013-03-08 01:34:06", updated_at: "2013-03-08 01:34:06">]
irb(main):004:0> kk << Klass.create(title: "Klass 2")
SQL (0.3ms) INSERT INTO "klasses" ("created_at", "title", "updated_at") VALUES (?, ?, ?) [["created_at", Fri, 08 Mar 2013 01:34:14 UTC +00:00], ["title", "Klass 2"], ["updated_at", Fri, 08 Mar 2013 01:34:14 UTC +00:00]]
=> [#<Klass id: 1, title: "Klass 1", created_at: "2013-03-08 01:34:06", updated_at: "2013-03-08 01:34:06">, #<Klass id: 2, title: "Klass 2", created_at: "2013-03-08 01:34:14", updated_at: "2013-03-08 01:34:14">]
irb(main):005:0> s.klasses = kk
Klass Load (0.1ms) SELECT "klasses".* FROM "klasses" INNER JOIN "student_klasses" ON "klasses"."id" = "student_klasses"."klass_id" WHERE "student_klasses"."student_id" = ? [["student_id", 1]]
SQL (0.4ms) INSERT INTO "student_klasses" ("created_at", "klass_id", "student_id", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Fri, 08 Mar 2013 01:34:29 UTC +00:00], ["klass_id", 1], ["student_id", 1], ["updated_at", Fri, 08 Mar 2013 01:34:29 UTC +00:00]]
SQL (0.1ms) INSERT INTO "student_klasses" ("created_at", "klass_id", "student_id", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Fri, 08 Mar 2013 01:34:29 UTC +00:00], ["klass_id", 2], ["student_id", 1], ["updated_at", Fri, 08 Mar 2013 01:34:29 UTC +00:00]]
=> [#<Klass id: 1, title: "Klass 1", created_at: "2013-03-08 01:34:06", updated_at: "2013-03-08 01:34:06">, #<Klass id: 2, title: "Klass 2", created_at: "2013-03-08 01:34:14", updated_at: "2013-03-08 01:34:14">]
irb(main):006:0> Student.first.klasses.map(&:id)
Student Load (0.2ms) SELECT "students".* FROM "students" ORDER BY "students"."id" ASC LIMIT 1
Klass Load (0.1ms) SELECT "klasses".* FROM "klasses" INNER JOIN "student_klasses" ON "klasses"."id" = "student_klasses"."klass_id" WHERE "student_klasses"."student_id" = ? [["student_id", 1]]
=> [1, 2]

Related

Find a record from another table with case insensitive combined [duplicate]

This question already has answers here:
Case-insensitive search in Rails model
(21 answers)
Closed 3 years ago.
I have these two tables:
product has_one address
address belongs_to product
When I want to find a product from one city, I do:
Product.all.joins(:address).where(addresses: {city: #city})
When I want to to find all cities with case insensitive is
Address.all.where('lower(city) like ?', #city.downcase)
But now I want a record from another table with case insensitive combined. How can I do ?
I tried some ways and nothing:
Product.all.joins(:address).where(addresses: {"lower(city) like ?", #city.downcase})
gives the error:
SyntaxError: unexpected '}', expecting end-of-input
...ower(city) like ?", "new york"})
...
and the other way:
Product.all.joins(:address).where(addresses: {"lower(city) like ?": #city.downcase})
gives nothing:
#<Product::ActiveRecord_Relation:0x3fb3fc142900>
Obs: I created another question because the old one was wrongly marked as duplicated.
Product.joins(:address).where "lower(addresses.city) like ?", #city.downcase
Response to comment
No idea what you're talking about "didn't track from the column city on addresses table" - what does "didn't track" mean?
Here's a console session with your models as described showing my code above working just as described:
[14] pry(main)> Address.create city: "New York", product: Product.first
Product Load (0.3ms) SELECT "products".* FROM "products" ORDER BY "products"."id" ASC LIMIT $1 [["LIMIT", 1]]
(0.1ms) BEGIN
Address Create (43.9ms) INSERT INTO "addresses" ("product_id", "city", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["product_id", 1], ["city", "New York"], ["created_at", "2019-02-27 18:57:37.688063"], ["updated_at", "2019-02-27 18:57:37.688063"]]
(0.3ms) COMMIT
=> #<Address:0x00007fb1e4322178 id: 1, product_id: 1, city: "New York", created_at: Wed, 27 Feb 2019 18:57:37 UTC +00:00, updated_at: Wed, 27 Feb 2019 18:57:37 UTC +00:00>
[15] pry(main)> #city = "NEW YORK"
=> "NEW YORK"
[16] pry(main)> Product.joins(:address).where "lower(addresses.city) like ?", #city.downcase
Product Load (0.7ms) SELECT "products".* FROM "products" INNER JOIN "addresses" ON "addresses"."product_id" = "products"."id" WHERE (lower(addresses.city) like 'new york')
=> [#<Product:0x00007fb1e52f5e10 id: 1, name: "First Product", created_at: Wed, 27 Feb 2019 18:57:07 UTC +00:00, updated_at: Wed, 27 Feb 2019 18:57:07 UTC +00:00>]

has_many through middle model not creating, but creating duplicate of one model

I have two models join by a middle model
class Integration < ActiveRecord::Base
has_many :integration_records
has_many :records, through: :integration_records
end
class IntegrationRecord < ActiveRecord::Base
belongs_to :integration
belongs_to :record
end
class Record < ActiveRecord::Base
has_many :integration_records
has_many :integrations, through: :integration_records
end
i = Integration.create(whatever)
i.records.create(whatever)
=> (0.1ms) BEGIN
SQL (8.7ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:02 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:02 UTC +00:00]]
SQL (0.6ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:06 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:06 UTC +00:00]]
(0.4ms) COMMIT
i.records
=> [one record]
Record.all.count
=> 2
i.integration_records
=> #<IntegrationRecord id: nil, integration_id: 1, record_id: 2 >
Notice id is nil
IntegrationRecord.all
=> #<ActiveRecord::Relation []>
IntegrationRecord.create
=> #<IntegrationRecord id: nil, integration_id: nil, record_id: nil>
Notice id is nil
Record.create.integrations.create
=> (0.1ms) BEGIN
SQL (8.7ms) INSERT INTO "integrations" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:02 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:02 UTC +00:00]]
SQL (0.6ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:06 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:06 UTC +00:00]]
(0.4ms) COMMIT
Not sure why this is happening, in the case of i.records.create(whatever) it should output:
SQL (0.6ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:06 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:06 UTC +00:00]]
(0.4ms) COMMIT
INSERT INTO "integration_records" ("created_at", "data", "integration_id", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Sat, 03 May 2014 15:57:05 UTC +00:00], ["data", {}], ["integration_id", 5], ["updated_at", Sat, 03 May 2014 15:57:05 UTC +00:00]]
I should note that for some reason, when I create a new app in rails 4.0.4, I still had this problem. But when I change the names of the models and specifically Record model, it works perfectly fine. So when I changed it to Recordrow no problem at all.
As per your current association setup, all you need to do is, just follow these simple instructions step by step
Create an Integration record
## this will create an "integration" record in "integrations" table
integration = Integration.create(whatever)
Create an associated Record record
## this will create an associated "record" entry in "records" table
## PLUS this will also created an associated record in "integration_records" table
integration.records.create(whatever)
View the associated records and associated integration_records
## Display all the "records" entries associated to this particular integration
integration.records
## Display all the "integration_records" entries associated to this particular integration
integration.integration_records
UPDATE
i.records.create(whatever) was creating 2 records, found out that the issue was with the name records of the table. Once changed everything works fine. It looks like records is reserved word.
Also, OP found this link which states records is reserved

MultiSelect Not Passing Multiple Values

I have a application that allows a user to upload a creative and assign it to multiple weeks
Week Model
class Week < ActiveRecord::Base
has_many :creative_weeks
has_many :creatives, :through => :creative_weeks
end
Creative Model
class Creative < ActiveRecord::Base
has_many :creative_weeks
has_many :weeks, :through => :creative_weeks
mount_uploader :image, CreativeUploader
end
Creative Weeks [Join Table]
class CreativeWeek < ActiveRecord::Base
belongs_to :week
belongs_to :creative
end
I know the association works which allows me to issue a creative to multiple weeks based on my console:
2.0.0p353 :020 > c = Creative.first
Creative Load (0.2ms) SELECT "creatives".* FROM "creatives" ORDER BY "creatives"."id" ASC LIMIT 1
=> #<Creative id: 10, name: "", account_id: 1, week_id: nil, campaign_id: 1, image: "Quakes_2013_DigiOOH_40YR_704x496.jpg", created_at: "2014-02-20 18:13:47", u
pdated_at: "2014-02-20 18:13:47">
2.0.0p353 :021 > c.week_ids
(0.2ms) SELECT "weeks".id FROM "weeks" INNER JOIN "creative_weeks" ON "weeks"."id" = "creative_weeks"."week_id" WHERE "creative_weeks"."creative_id" = ? [["
creative_id", 10]]
=> [3]
2.0.0p353 :022 > c.week_ids = [1, 2, 3]
Week Load (0.2ms) SELECT "weeks".* FROM "weeks" WHERE "weeks"."id" IN (1, 2, 3)
Week Load (0.1ms) SELECT "weeks".* FROM "weeks" INNER JOIN "creative_weeks" ON "weeks"."id" = "creative_weeks"."week_id" WHERE "creative_weeks"."creative_id"
= ? [["creative_id", 10]]
(0.1ms) begin transaction
SQL (0.3ms) INSERT INTO "creative_weeks" ("created_at", "creative_id", "updated_at", "week_id") VALUES (?, ?, ?, ?) [["created_at", Thu, 20 Feb 2014 18:20:27
UTC +00:00], ["creative_id", 10], ["updated_at", Thu, 20 Feb 2014 18:20:27 UTC +00:00], ["week_id", 1]]
SQL (0.1ms) INSERT INTO "creative_weeks" ("created_at", "creative_id", "updated_at", "week_id") VALUES (?, ?, ?, ?) [["created_at", Thu, 20 Feb 2014 18:20:27
UTC +00:00], ["creative_id", 10], ["updated_at", Thu, 20 Feb 2014 18:20:27 UTC +00:00], ["week_id", 2]]
(1.0ms) commit transaction
=> [1, 2, 3]
2.0.0p353 :023 >
The issue I am having is getting this same functionality to work on the front end. It will only pass in one value, typically the last one chosen
In my form:
<div class="field">
<%= f.collection_select(:week_ids, Week.all, :id, :start_at, {}, multiple: true, name: 'creative[week_ids]') %>
</div>
Can anyone advise me what I am missing?
TIA
Change the select tag's name to this:
name: 'creative[week_ids][]'
That extra '[]' at the end of the name specifies that you want an array of values to be posted.
Using strong parameters you have to specify that the value will be an array:
def your_strong_params
params.require(:creative).permit(week_ids: [])
end

Rails Engines - is it possible to add associations to the model in a container model like Forem does

This Question is more than a single question so breaking it up into more managable pieces: Rails Engines - simple possible engine to (1) add a model and (2) add the association in the containing class
I am testing out building a Rails engine and am curious whether I can add an association to a specific model in the hosting / container app.
The hosting app has a user model class (yes, this will never chnage) and my engine is called abc and I have a model in my engine called posts (so Abc::Post and the table is abc_posts). I'd like to add to the User class in the main app this association. As a drop dead simple try, I created in my engine:
#located in the engine at: abc/app/models/user.rb
class User < ActiveRecord::Base
has_many :abc_posts
end
the post file:
#located in the engine at: abc/app/models/abc/post.rb
module Abc
class Post < ActiveRecord::Base
attr_accessible :body, :header, :user_id
belongs_to :user
end
end
Via rails console, I was able to create records in the table (easy part) but the User class doesn't know about the association. Any ideas on how to get this done?
thx in advance
edit 1
I've tried using the decorators gem as used in forem (see comment below) and have this file:
#abc/app/decorators/lib/abc/user_class_decorator.rb
Object.const_get(User).class_eval do
has_many :abc_posts, :class_name => "Abc::Post", :foreign_key => "user_id"
end
I have included the decorators via:
lib/abc.rb
require "decorators"
but his doesn't seem to be working. Not sure if this is right strategy or whether syntax is even correct.
That should do the job - specify the class for the relationship:
class User < ActiveRecord::Base
has_many :posts, :class_name => "Abc::Post"
end
Hmmm, I created an example and it does work ...
class Parent < ActiveRecord::Base
has_many :children, :class_name => "Abc::Child"
end
The module with the class Child is in the model/abc.
module Abc
class Child < ActiveRecord::Base
belongs_to :parent
end
end
Here the journal
1.9.3-p194 :001 > Parent.create(:name => 'Mr Daddy')
(0.1ms) begin transaction
SQL (9.4ms) INSERT INTO "parents" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", Fri, 03 May 2013 10:49:54 UTC +00:00], ["name", "Mr Daddy"], ["updated_at", Fri, 03 May 2013 10:49:54 UTC +00:00]]
(1.9ms) commit transaction
=> #<Parent id: 1, name: "Mr Daddy", created_at: "2013-05-03 10:49:54", updated_at: "2013-05-03 10:49:54">
1.9.3-p194 :002 > Abc::Child.create(:name => 'Sammy boy', :parent => Parent.first )
Parent Load (0.3ms) SELECT "parents".* FROM "parents" ORDER BY "parents"."id" ASC LIMIT 1
(0.1ms) begin transaction
SQL (117.3ms) INSERT INTO "children" ("created_at", "name", "parent_id", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Fri, 03 May 2013 10:49:58 UTC +00:00], ["name", "Sammy boy"], ["parent_id", 1], ["updated_at", Fri, 03 May 2013 10:49:58 UTC +00:00]]
(2.1ms) commit transaction
=> #<Abc::Child id: 1, name: "Sammy boy", parent_id: 1, created_at: "2013-05-03 10:49:58", updated_at: "2013-05-03 10:49:58">
1.9.3-p194 :003 > Abc::Child.create(:name => 'Milly girl', :parent => Parent.first )
Parent Load (0.3ms) SELECT "parents".* FROM "parents" ORDER BY "parents"."id" ASC LIMIT 1
(0.2ms) begin transaction
SQL (0.8ms) INSERT INTO "children" ("created_at", "name", "parent_id", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Fri, 03 May 2013 10:50:15 UTC +00:00], ["name", "Milly girl"], ["parent_id", 1], ["updated_at", Fri, 03 May 2013 10:50:15 UTC +00:00]]
(2.7ms) commit transaction
=> #<Abc::Child id: 2, name: "Milly girl", parent_id: 1, created_at: "2013-05-03 10:50:15", updated_at: "2013-05-03 10:50:15">
1.9.3-p194 :004 > Parent.first.children.first
Parent Load (0.4ms) SELECT "parents".* FROM "parents" ORDER BY "parents"."id" ASC LIMIT 1
Abc::Child Load (0.3ms) SELECT "children".* FROM "children" WHERE "children"."parent_id" = ? ORDER BY "children"."id" ASC LIMIT 1 [["parent_id", 1]]
=> #<Abc::Child id: 1, name: "Sammy boy", parent_id: 1, created_at: "2013-05-03 10:49:58", updated_at: "2013-05-03 10:49:58">
1.9.3-p194 :005 > Parent.first.children.last
Parent Load (0.5ms) SELECT "parents".* FROM "parents" ORDER BY "parents"."id" ASC LIMIT 1
Abc::Child Load (0.4ms) SELECT "children".* FROM "children" WHERE "children"."parent_id" = ? ORDER BY "children"."id" DESC LIMIT 1 [["parent_id", 1]]
=> #<Abc::Child id: 2, name: "Milly girl", parent_id: 1, created_at: "2013-05-03 10:50:15", updated_at: "2013-05-03 10:50:15">
1.9.3-p194 :006 > Parent.first.children.count
Parent Load (0.3ms) SELECT "parents".* FROM "parents" ORDER BY "parents"."id" ASC LIMIT 1
(0.3ms) SELECT COUNT(*) FROM "children" WHERE "children"."parent_id" = ? [["parent_id", 1]]
=> 2

rails 3 create sets values to bill

So after a merge, my RoR 3 project no longer does 'creates' correctly. Default attributes get set correctly, but not the ones I pass in:
1.9.3-p125 :020 > f=Ifilter.create(:name => "test2", :regex => "()" )
SQL (101.5ms) INSERT INTO "ifilters" ("created_at", "name", "regex", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Sat, 10 Mar 2012 03:36:24 UTC +00:00], ["name", nil], ["regex", nil], ["updated_at", Sat, 10 Mar 2012 03:36:24 UTC +00:00]]
=> #<Ifilter id: 2, name: nil, regex: nil, created_at: "2012-03-10 03:36:24", updated_at: "2012-03-10 03:36:24">
However, save still works:
1.9.3-p125 :021 > f=Ifilter.new
=> #<Ifilter id: nil, name: nil, regex: nil, created_at: nil, updated_at: nil>
1.9.3-p125 :022 > f.name = "test"
=> "test"
1.9.3-p125 :023 > f.regex = "()"
=> "()"
1.9.3-p125 :024 > f.save
SQL (4.8ms) INSERT INTO "ifilters" ("created_at", "name", "regex", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Sat, 10 Mar 2012 04:13:10 UTC +00:00], ["name", "test"], ["regex", "()"], ["updated_at", Sat, 10 Mar 2012 04:13:10 UTC +00:00]]
=> true
What's going on?
Thanks!
Looks like you may have attr_accessible on your model. http://api.rubyonrails.org/classes/ActiveModel/MassAssignmentSecurity/ClassMethods.html#method-i-attr_accessible
If you are using it, attributes that aren't explicitly listed won't get set via #new, #create, and #update_attributes (and a few more).
This usually happens when you have attribute whitelisting enabled. Make sure you define the attributes that are settable via mass-assignment in your model via the attr_accessible macro:
class Ifilter
attr_accessible :name, :regex
...
end

Resources