This kind of goes along with my original question about has_many :through with conditions, but I've found another problem.
Given this class:
class Contact < AR
has_many :group_contacts
has_many :groups, :through => :group_contacts, :conditions => {:published => true}
has_many :anonymous_groups, :through => :group_contacts, :source => :group, :conditions => {:anonymous => true}
end
My problem happens when I try to include the anonymous_groups with contacts:
Contact.includes(:anonymous_groups)
ActiveRecord::StatementInvalid: PGError: ERROR: missing FROM-clause entry for table "contacts"
The reason for this is the generated sql is incorrect. It's something akin to:
SELECT "group_contacts"."id" AS t0_r0 ... "groups"."anonymous" AS t1_r5 ... LEFT OUTER JOIN "groups" ON "groups"."id" = "group_contacts"."group_id" WHERE ("group_contacts".contact_id IN (...) AND ("contacts"."anonymous" = 'true'))
Paraphrased of course, but look at the final condition. It's put the anonymous condition on contacts rather than groups
Now, we can alleviate this query error (which i'm sure is a bug) but doing something like:
has_many :anonymous_groups, :through => :group_challenges, :source => :group, :conditions => { :groups => {:anonymous => :true} }
This puts the condition on the correct table in sql, but when I try to build an anonymous group, I get this:
contact.anonymous_groups.build
ActiveRecord::UnknownAttributeError: unknown attribute: groups.anonymous
So it works for querying but not for building. I'm quite certain this is a bug, but I'm wondering if anyone else has experienced this or has a workaround.
I think something like :conditions => "groups.anonymous = true" should work fine.
Related
Is this wrong to have that same foreign key for two different models? It's Rails 4.0.0 app so conditions are written like that. I ask because I got some problem with blinks and can't find it.
has_many :messages, :conditions => {:deleted => false, :subject_h => ''}
has_many :messages_send, :class_name => "Message", :foreign_key => "sender_id", :conditions => ['deleted_sender = ?', false]
has_many :blinks, :conditions => {:deleted => false, :subject_h => ''}
has_many :blinks_send, :class_name => "Blink", :foreign_key => "sender_id", :conditions => ['deleted_sender = ?', false]
I would do one of the two following things:
1 - Have one table per model (so one blinks table and one messages table).
2 - Use Single Table Inheritance (STI) for each messages. You would have only one table (senders in your case), with a type field that would differentiate between a blink and a message.
For simplicity here, I would personally go for the first option, and evolve to the STI later on if still needed.
The advantage would also be that you'll be able to declare your associations like this:
has_many :messages
has_many :blinks
I have some model classes like this:
class Organisation < ActiveRecord::Base
has_many :dongles
has_many :licences_on_owned_dongles, :through => :dongles, :source => :licences,
:include => [:organisation, :user, :owner_organisation, :profile, :dongle,
{:nested_licences => [:profile]} ]
end
class Dongle < ActiveRecord::Base
has_many :licences
belongs_to :organisation
end
class Licence < ActiveRecord::Base
belongs_to :dongle
# tree-like structure. I don't remember why this had to be done but the comment says
# "find a way to make the simpler way work again" and I tried using the simpler way
# but tests still fail. So obviously the SQL awfulness is necessary...
default_scope :conditions => { :parent_licence_id, nil }
has_many :nested_licences, :class_name => 'Licence', :dependent => :destroy,
:autosave => true,
:foreign_key => :parent_licence_id,
:finder_sql => proc {
"SELECT l.* FROM licences l WHERE l.parent_licence_id = #{id}" },
:counter_sql => proc {
"SELECT COUNT(*) FROM licences l WHERE l.parent_licence_id = #{id}" }
end
Now I can do this:
test "getting licences on owned dongles" do
org = organisations(:some_other_corp)
assert_equal [licences(:licence_4)], org.licences_on_owned_dongles
end
That happily passes. Since it's an association, you might thing you can find() on it:
test "getting licences on owned dongles and then filtering further" do
org = organisations(:some_other_corp)
conditions = { :owner_organisation_id => nil }
assert_equal [licences(:licence_4)],
org.licences_on_owned_dongles.find(:all, :conditions => conditions)
end
But this gives:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: dongles.organisation_id: SELECT "licences".* FROM "licences" WHERE "licences"."parent_licence_id" IS NULL AND (("dongles".organisation_id = 72179513)) AND ("licences".parent_licence_id = 747059259)
test/unit/organisation_test.rb:123:in `test_getting_licences_on_owned_dongles_and_then_filtering_further'
In fact, this even occurs when all you call is find(:all). It isn't just SQLite either, because I noticed this in production (oops) on MySQL.
So I don't know. It's really too mysterious to investigate further. I might shelve it as a "Rails just can't do find() on an association", use a block to filter it and leave it at that. But I wanted to put it out, just in case there is a better option.
(Actually if you look at the query Rails is generating, it is complete nonsense. Somehow it has ended up generating a query where something has to be NULL and equal to a value at the same time. Even if the query worked, this will return 0 rows.)
Don't use find in a Rails 3 app.
org.licences_on_owned_dongles.find(:all, :conditions => conditions)
should be
org.licences_on_owned_dongles.where(conditions)
Edit: Read up on it here.
I think you're looking for .where:
org.licenses_on_owned_dongles.where(conditions)
I'm trying to setup a has_many with conditions which works fine for the reading part but not for new entries. I've tested it some weeks ago in a sandbox and it worked but I can't get it work again so maybe I'm just blind or it is just a wrong design :-)
class Task
has_many :task_users
has_many :assignees, :through => :task_users, :source => :user, :conditions => {"task_users.is_assignee" => true}
has_many :participants, :through => :task_users, :source => :user
end
class TaskUser < ActiveRecord::Base
belongs_to :user
belongs_to :task
end
class User
has_many :tasks
end
After adding a new assignee to a task like this
Task.first.assignees << User.first
the following SQL is executed
SQL (0.3ms) INSERT INTO `task_users` (`created_at`, `is_assignee`, `task_id`, `updated_at`, `user_id`) VALUES ('2012-11-18 15:52:24', NULL, 2, '2012-11-18 15:52:24', 3)
I thought rails will use my conditions to set these values when I'm add ing new ones. Reading works great but I have no idea why adding new values doesn't work with conditions.
I expect this INSERT
SQL (0.3ms) INSERT INTO `task_users` (`created_at`, `is_assignee`, `task_id`, `updated_at`, `user_id`) VALUES ('2012-11-18 15:52:24', 1, 2, '2012-11-18 15:52:24', 3)
I'm not entirely sure whether you can specify :conditions hash on the join table in a has_many :through association. Someone else correct me if I'm wrong, but the condition has to be directly on the source association, :user in your case.
If this is the case, to work around this you can specify an auxiliary association:
has_many :task_users
has_many :assignee_task_users, :class_name => 'TaskUser', :conditions => {"is_assignee" => true}
has_many :assignees, :through => :assignee_task_users, :source => :user
just going to highlight the documentation:
Specify the conditions that the associated objects must meet in order to be
included as a WHERE SQL fragment, such as price > 5 AND name LIKE 'B%'.
Record creations from the association are scoped if a hash is used.
has_many :posts, :conditions => {:published => true} will create published
posts with #blog.posts.create or #blog.posts.build.
even though you used an hash already, the first parameter is a string, which is unneded to be (the association knows already the table name). rewrite it as :conditions => {:is_assignee => true} and it should work.
Also, the way you are creating users should be rewritten in order for this to work of course. Instead of:
Task.first.assignees << User.first
use:
Task.first.assignees.create
and that should do the trick.
A CertProgramItem has_many :cert_schedules.
A CertSchedule belongs_to :reg_fee_item, :foreign_key => 'reg_fee_item_id', :class_name => 'Item'
Starting with the CertProgramItem, I want to get all CertSchedules and their related tables in one query (to avoid the n+1 problem). My first query was:
cpi_arr = CertProgramItem.find(:all, :include => :cert_schedules, :order => :id)
However, this didn't fetch the members of the Item class which belong to the collection of CertSchedules.
I have modified the query:
cpi_arr = CertProgramItem.find(:all, :include => {:cert_schedules => :items}, :order => :id)
and
cpi_arr = CertProgramItem.find(:all, :include => {:cert_schedules => :reg_fee_items}, :order => :id)
but I get errors like ActiveRecord::ConfigurationError: Association named 'items' was not found; perhaps you misspelled it?" or ActiveRecord::ConfigurationError: Association named 'reg_fee_items' was not found; perhaps you misspelled it? for the 2nd.
Is there a way to get this nested, foreign-key association in one query?
Here's some more detailed information on the CertSchedule assocciations:
class CertSchedule < ActiveRecord::Base
belongs_to :cert_program_item
belongs_to :reg_fee_item, :foreign_key => 'reg_fee_item_id', :class_name => 'Item'
belongs_to :start_term, :class_name => 'SchoolTerm', :foreign_key => 'start_term_id'
My latest version of the query looks like this:
cpi_arr = CertProgramItem.find(:all, :include => [:cert_tier, {:cert_schedules => [:reg_fee_item,:start_term] }])
This query is now successfully returning what I expected. Lessons learned:
Use the foreign key name from the model, not the actual table name.
Multiple items in an association need to be surrounded with square brackets [].
I've got the following models:
project.rb
has_many :tasks
task.rb
belongs_to :project
has_many :assignments
has_many :users, :through => :assignments
user.rb
has_many :assignments
has_many :tasks, :through => :assignments
assignment.rb
belongs_to :task
belongs_to :user
So for example:
Project.first.title #=> "Manhattan"
Project.first.tasks.map(&:name) # => ['Find Scientists', 'Find Money', 'Find Location']
Project.first.tasks.first.users.map(&:full_name) #=> ['James Maxwell', 'Evariste Galois', 'Jules Verne']
My first question is:
How can I find all the persons' names possibly with symbol to proc in one shot, I tried:
Project.first.tasks.users.full_name #=> AND FAILED
Project.first.tasks.map(&:users).full_name #=> AND FAILED
Project.first.tasks.map(&:users).map(&:full_name) #=> AND FAILED
Any ideas?
And I think this following question might be in the same ball park:
How can I do a find of Project with conditions that search the 'full_name' attribute of the users its tasks?
Example
Project.all(:include => {:tasks => :users}, :conditions => ['tasks.users.full_name LIKE ?', query]) #this failed
I think the problem is at the 'tasks.users'.
Thanks everyone, have a happy thanksgiving!
For the first one you'll want to do something like this:
Project.first.tasks.map { |t| t.users.map(&:full_name) }.flatten
The reason for this is that you want to iterate through all the tasks, then all the users in each task. Without the flatten this would give you a 2-dimensional array.
And for the second one your find should be:
Project.all(:include => {:tasks => :users}, :conditions => ['users.full_name LIKE ?', query])
Writing users.full_name implies to the SQL engine that you're looking for the full_name field on the users table.