map method in many_to_many relationship - ruby-on-rails

I have the following 3 models in my application:
class Submission < ActiveRecord::Base
has_many :linkedsubmissions
end
class Linkedsubmission < ActiveRecord::Base
belongs_to :submission
has_many :lnksubtypes
end
class Lnksubtype < ActiveRecord::Base
belongs_to :linkedsubmission
end
In the code below '#submission.linkedsubmissions.lnksubtypes' is incorrect.
#history = Audit.find(:all, :conditions => ["auditable_id IN (?)",#submission.linkedsubmissions.lnksubtypes.map{|b| b.LSU_ID} ])
I need to find all audits with 'auditable_id' in #submission.linkedsubmissions.lnksubtypes

You need to add the following has_many relationship to your Submission model.
class Submission < ActiveRecord::Base
has_many :linkedsubmissions
has_many :lnksubtypes, :through => :linkedsubmissions
end
Now, you'll be able to reformat your query like this
#history = Audit.find(:all, :conditions => ["auditable_id IN (?)", #submission.lnksubtypes.map(&:LSU_ID) ])

Audit.find(:all, :conditions => ["auditable_id IN (?)",#submission.linkedsubmissions.map{|b| b.lnksubtypes.map{&:LSU_OID) }.flatten ])
But there should be a better way to get those LSU_OID with sql only.

#history = Audit.where(:auditable_id => #submission.lnksubtypes.map(&:LSU_ID))
slightly shorter

Related

Rails 4, How to get through - has_many associations active records

I modeling a three tables
Event, EventEventCategory and EventCategory
class Event < ActiveRecord::Base
has_many :event_event_categories
has_many :event_categories, :through => :event_event_categories
scope :live_event, ->{where(visible_status: 1)}
end
class EventEventCategory < ActiveRecord::Base
belongs_to :event
belongs_to :event_category
end
class EventCategory < ActiveRecord::Base
has_many :event_event_categories
has_many :events, :through => :event_event_categories
end
And I get a event using live_event scope.
#events = Event.live_event
And I wanna get a live_event 's category_lists
Now I get a Category list usgin below code
EventCategory.where("id in (?)", EventEventCategory.where("id in (?)", #events.pluck(:id).uniq))
or
EventCategory.where("id in (?)", #events.joins(:event_event_categories).pluck(:event_category_id).uniq)
is another command exist get a events category's list like a
Event.live_events.event_categories
EventCategory.joins(:events).where(events: { visible_status: 1 })
maybe this one can help

Reusing activerecord scopes on has many through associations

Say I have a few activerecord models in my rails 3.1 project that look like this:
class Component < ActiveRecord::Base
has_many :bugs
end
class Bug < ActiveRecord::Base
belongs_to :component
belongs_to :project
scope :open, where(:open => true)
scope :closed, where(:open => false)
end
class Project < ActiveRecord::Base
has_many :bugs
has_many :components_with_bugs, :through => :bugs, :conditions => ["bugs.open = ?", true]
end
In Short: I have a has_many through association (components_with_bugs) where I want to scope the "through" model. At present I'm doing this by duplicating the code for the scope.
Is there any way to define this has many through association (components_with_bugs) such that I can reuse the Bug.open scope on the through model, while still loading the components in a single database query? (I'm imagining something like :conditions => Bug.open)
Rails 4 answer
Given you have:
class Component < ActiveRecord::Base
has_many :bugs
end
class Bug < ActiveRecord::Base
belongs_to :component
belongs_to :project
scope :open, ->{ where( open: true) }
scope :closed, ->{ where( open: false) }
end
You have two possibilities:
class Project < ActiveRecord::Base
has_many :bugs
# you can use an explicitly named scope
has_many :components_with_bugs, -> { merge( Bug.open ) }, through: :bugs, source: 'component'
# or you can define an association extension method
has_many :components, through: :bugs do
def with_open_bugs
merge( Bug.open )
end
end
end
Calling projet.components_with_bugs or project.components.with_open_bugs will fire the same sql query:
SELECT "components".* FROM "components"
INNER JOIN "bugs" ON "components"."id" = "bugs"."component_id"
WHERE "bugs"."project_id" = ? AND "bugs"."open" = 't' [["project_id", 1]]
Which one is better to use depends on your application. But if you need to use many scopes on the same association, I guess association extensions could be clearer.
The real magic is done with merge which allows you to, as the name says, merge conditions of another ActiveRecord::Relation. In this case, it is responsible for adding AND "bugs"."open" = 't' in the sql query.
Apart from your scopes , write the default scope as:
default_scope where(:open => true) in your "through" model Bug.
class Bug < ActiveRecord::Base
belongs_to :component
belongs_to :project
default_scope where(:open => true)
scope :open, where(:open => true)
scope :closed, where(:open => false)
end
And in the Project model remove :conditions => ["bugs.open = ?", true]
class Project < ActiveRecord::Base
has_many :bugs
has_many :components_with_bugs, :through => :bugs
end
I think the above will work for you.
Try using the following.
has_many :components_with_bugs, :through => :bugs do
Bug.open
end
Can't you use something like this ?
has_many :components_with_bugs, :through => :bugs, :conditions => Bug.open.where_values
I haven't tested it, just proposing an path for investigation
The http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html specifies
:conditions Specify the conditions that the associated object must meet in order to be included as a WHERE SQL fragment, such as authorized = 1.
Hence you can do it as:
class Project < ActiveRecord::Base
has_many :bugs
has_many :components_with_bugs, :through => :bugs do
def open
where("bugs.open = ?", true)
end
end
end
EDIT:
You can't specify another model's scope as a condition. In your case, they way you have it implemented is right. You can implement it another way as
has_many :components_with_bugs, :through => :bugs # in this case, no need to use the relation.
def open_bugs
self.bugs.openn # openn is the scope in bug. Don't use name 'open'. It's a private method of Array.
end

ActiveRecord query assistance

i need a little help with a AR query. This is how my models look like:
class User < AR:B
has_many :publications
end
class Publication < AR:B
belongs_to :user
belongs_to :category
end
class Category < AR:B
has_many :publications
end
Now let's say I want to iterate over all existing categories and either display the user's publications, or display something like "#{current_user.name} has no publications in this category".
class PublicationsController < AC:B
def index
#categories = Category.find(:all, :include => :publications, :conditions => { 'publications.user_id' => current_user })
end
end
This gives me all Categories the user actually has publications, but lacks the "empty" ones.
Any suggestions? :-)
This gives you all the Category objects:
#categories = Category.all
Then, if you declare has_many :through associations you can do something like the following:
#categories.each do |category|
if category.users.include?(current_user)
# User has publications
publications = category.publications.select { |pub| pub.user == current_user }
else
# User has no publications
end
end
(has-many-through declarations:
class User < AR:B
has_many :publications
has_many :categories, :through => :publication
end
class Publication < AR:B
belongs_to :user
belongs_to :category
end
class Category < AR:B
has_many :publications
has_many :users, :through => :publication
end
... warning: drycode)
There's probably an neater way to do this using named scopes though.
You might be able to just modify the find call:
#categories = Category.find(:all, :include => :publications, :conditions => [ 'publications.user_id=? OR publications.user_id IS NULL', current_user ])
Notice that we use the Array variant here rather than the Hash variant, since the example in the documentation implies this is the correct usage.

Scopes with has many through association

I have 2 models Widget and Feature which have a has many through association using WidgetFeature model.
class Widget < ActiveRecord::Base
has_many :widget_features
has_many :features, :through => :widget_features
end
class WidgetFeature < ActiveRecord::Base
belongs_to :feature
belongs_to :widget
attr_accessible :children_features, :widget_id, :feature_id
end
class WidgetFeature < ActiveRecord::Base
belongs_to :feature
belongs_to :widget
attr_accessible :children_features, :widget_id, :feature_id
end
I have a widget_id.
So i do Widget.find_by_id(widget_id)
Now i want to find all the features for this widget where widget_features.children_features IS NULL.
I dont know how to do this, help me out.
Try
#widget = Widget.find_by_id(widget_id)
#features = #widget.features.conditions("widget_features.children_features IS nil")
EDITED
Ref this
has_many :features, :through => :widget_features, :conditions=>["widget_features.children_features is nil"]
AND then
#widget = Widget.find_by_id(widget_id)
#features = #widget.features
Feature.all(:joins => :widget_features, :conditions => ["widget_id = ? and children_features is null", some_id])
I worked my way around named_scope and found an elegant solution. So, I am posting it here so that others stuck with the same problem can also get help.
My solution gives you a way to access any column of the join model in the has many through association.
Here's the solution for my problem above:
class Widget < ActiveRecord::Base
has_many :widget_features
has_many :features, :through => :widget_features
def leaf_features
widget_features.leaf_features.map{|widget_feature|widget_feature.feature}
end
end
class WidgetFeature < ActiveRecord::Base
named_scope :leaf_features, :conditions => 'children_features IS NULL'
belongs_to :feature
belongs_to :widget
attr_accessible :children_features, :widget_id, :feature_id
end
Now, Widget.find_by_id(widget_id).leaf_features
will give you only those features where
children_features column is NULL.

Recursive :include in Rails ActiveRecord

Say I have these models
class Project < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :project
belongs_to :user
end
class User < ActiveRecord::Base
has_many :comments
end
So that I can do
p = Project.find(1, :include => :comments)
p.comments.collect(&:user).collect(&:name) # this executes select for each user
How do I say I want to also include comment's user?
I believe :include => {:comments => :user} should work.

Resources