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
Related
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.
I want to limit my association to two (one of every scope). I tried:
has_many :associations
has_many :associations_with_first_scope, :class_name => 'Association', :conditions => {...}
has_many :associations_with_second_scope, :class_name => 'Association', :conditions => {...}
validates :associations, {:maximum => 4}
validates :associations_with_first_scope, {:maximum => 2}
validates :associations_with_second_scope, {:maximum => 2}
and I also tried custom validator (i tried count, size and length also):
validate :custom_associations_limit
def custom_associations_limit
error.add(:base, '...') if associations_with_first_scope.size > 2
error.add(:base, '...') if associations_with_second_scope.size > 2
end
I also tried to put validation into association model. Nothing works. I think my problem caused by using nested_form gem. When I have form with four associations (two of each kind) and I'll delete tow and add tow, model thinks it has six associations instead of four. Because it is validating probably before nested_attributes are passed (with allow_destroy set to true).
Any can help with this?
Rails already has this built-in with the has_one macro.
has_many :associations
has_one :associations_with_first_scope, :class_name => 'Association', :conditions => {...}
has_one :associations_with_second_scope, :class_name => 'Association', :conditions => {...}
The only difference is that you won't be using a plural association since there will only ever be one.
I've got multiple has_one associations in my model
class Invoice < ActiveRecord::Base
...
belongs_to :seller, :class_name => "Client", :foreign_key => "provider", :conditions => ['is_provider = ?', true]
belongs_to :customer, :class_name => "Client", :foreign_key => "receiver", :conditions => ['is_receiver = ?', true]
I'm trying to index it:
indexes [seller(:tbClientCode), seller(:tbClientLabel), seller(:tbClientName), seller(:tbClientNIP),
seller(:tbClientRegon), seller(:tbClientZip), seller(:tbClientCity), seller(:tbClientStreet),
seller(:tbClientHouseNr), seller(:tbClientHomeNr], :sortable => true, :as => :seller_fields
has [seller(:is_provider), seller(:is_receiver)], :sortable => true, :as => :seller_attributes
indexes [customer(:tbClientCode), customer(:tbClientLabel), customer(:tbClientName), customer(:tbClientNIP),
customer(:tbClientRegon), customer(:tbClientZip), customer(:tbClientCity), customer(:tbClientStreet),
customer(:tbClientHouseNr), customer(:tbClientHomeNr], :sortable => true), :as => :customer_fields
has [customer(:is_provider), customer(:is_receiver)], :sortable => true, :as => :customer_attributes
... and then some error occurs
ERROR: index 'invoice_core': sql_range_query: ERROR: column reference "is_receiver" is ambiguous
LINE 1: ...tomers_invoices"."id" = "invoices"."receiver" AND is_receive...
Changing syntax from customer(:tbClientName) to customer.tbClientName is not solution.
Some help would be greatly appreciated
Well, firstly: I don't think you actually want to combine all those columns into just one field and just one attribute?
So, remove the [] - you can pass in multiple columns, but if you pass in an explicit array, then that'll group those columns together into a single field/attribute. Thus, remove the :as option as well - you don't want all of those columns together having the same name, the generated SQL statement won't make any sense.
Secondly: attributes are sortable by their very nature, so you don't need to pass :sortable => true to the has call. Also: do you really need all of the fields to be sortable? Better to only mark the fields that should be sortable as such.
But lastly, and perhaps most importantly: the error that's actually happening is because of your conditions hash for your two associations. You're joining on the same table twice, but you don't have any table references in the conditions. Try changing both conditions settings to the following:
:conditions => {:is_provider => true}
:conditions => {:is_receiver => true}
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 have a Model which has a belongs_to association with another Model as follows
class Article
belongs_to :author, :class_name => "User"
end
If I wanted to find all articles for a particular genre ordered by author I would do something like the following
articles = Article.all(:includes => [:author], :order => "users.name")
However if Article happens to have two references to User how can I sort on :author?
class Article
belongs_to :editor, :class_name => "User"
belongs_to :author, :class_name => "User"
end
I have tried
articles = Article.all(:includes => [:author], :order => "users.name")
#=> incorrect results
articles = Article.all(:includes => [:author], :order => "authors.name")
#=> Exception Thrown
My first attempted solution
A half solution is as follows. It was not totally obvious but looking at my logs I figured it out.
class Article
belongs_to :editor, :class_name => "User"
belongs_to :author, :class_name => "User"
end
Basically you need to do the following
articles = Article.all(:include => [:editor,:author], :order => 'articles_authors.name')
articles = Article.all(:include => [:editor,:author], :order => 'authors_articles.name')
It is the naming of the alias that I missed (articles_authors)
The issue with this is that the following does not work although it seems like it should.
articles = Article.all(:include => [:editor,:author], :order => 'authors_articles.name')
articles = Article.all(:include => [:editor,:author], :order => 'editors_articles.name')
This may be an issue if you have a UI table and want to send the order field to the controller. So you may want to first order on author then editor. But it would fail for for one of the queries (unless you dynamically change the include too)
Update - Added this to the original Question.
So I think I have nailed it. It was not totally obvious but looking at my logs I figured it out.
class Article
belongs_to :editor, :class_name => "User"
belongs_to :author, :class_name => "User"
end
Basically you need to do the following
articles = Article.all(:include => [:editor,:author], :order => 'articles_authors.name')
articles = Article.all(:include => [:editor,:author], :order => 'authors_articles.name')
It is the naming of the alias that I missed (articles_authors)
This is called Table Aliasing in ActiveRecord. When the find method joins the same table more than once the alias names for the table is determined as follows:
Active Record uses table aliasing in the case that a table is referenced
multiple times in a join. If a table is referenced only once, the standard table
name is used. The second time, the table is aliased as
#{reflection_name}_#{parent_table_name}. Indexes are appended for any more
successive uses of the table name.
Refer to the ActiveRecord documentation for more details. Search for Table Aliasing to navigate to the specific section.