Using has_many => through association.
Here is what i have.
:planning model
has_many :acttypes
has_many :actcategories
has_many :acts, :through => :actcategories
:acts model
belongs_to :acttype
has_many :actcategories
has_many :plannings, :through => :actcategories
:actcategories model
named_scope :theacts, lambda { |my_id|
{:conditions => ['planning_id = ?', my_id] }}
belongs_to :act
belongs_to :planning
:acttype model
has_many :acts
My Problem Starts here. I need to show all Acts by each Act Type from Plannings that is part of the actcategories association
Right now i am getting all the acts and missing the actcategories association.
Planning Controller
def show
#planning = Planning.find(params[:id])
#acttypes = Acttype.find(:all, :include => :acts)
#acts = Actcategory.theacts(#planning)
end
Planning Show View
<% #acttypes.each do |acttype|%>
<%= acttype.name %>
<% #acts.each do |acts| %>
<li><%= link_to acts.act.name, myacts_path(acts.act, :planning => #planning.id) %></li>
<% end %>
<% end -%>
Thanks for any help.
I think the key thing you're missing is that finders and named scopes only return the Class that they're called on.
#acts = Actcategory.theacts(#planning)
#acts is all the Actcategories where actcategories.planning_id = #planning.id. They don't necessarily have the required act type.
Really, what I think you're looking for is this named scope:
class Act < ActiveRecord::Base
named_scope :with_planning, lambda do |planning_id|
{ :joins => :actcategories,
:conditions => {:actcategories => {:planning_id => planning_id}}
}
...
end
Which limits acts to those associated with the given planning. This can be called on an association to limit the linked acts to those associated with a specific planning.
Example: #acts contains acts of acttype, x, that are associated with planning, y.
#acts = Acttype.find(x).acts.with_planning(y)
With this named scope this code should accomplish what you were aiming for.
controller:
def show
#planning = Planning.find(params[:id])
#acttypes = Acttype.find(:all, :include => :acts)
end
view:
<% #acttypes.each do |acttype| %>
<h2> <%= acttype.name %><h2>
<% acttype.acts.with_planning(#planning) do |act| %>
This act belongs to acttype <%= acttype.name%> and
is associated to <%=#planning.name%> through
actcatgetories: <%=act.name%>
<%end%>
<%end%>
Related
I am trying to refine my article and giving my user flexibility to decide what they want to view.
Here the models with relationship
Article
has_many :tags, through: :articletags
ArticleTags
belongs_to :article
belongs_to :tags
Tags
has_many :article, through: articletags
Now the idea is the use would go in article and on the side see the tags.title which then give refresh the pages with Article where tags = "world". Now i am trying to do this with scope but i am not to sure how to do it. Here my scope in my model
scope :by_tags, where(title => ?, "world news")
Here how i call it
<%= link_to (tag.title), articles_path(:scope => "test") %>
But obviously it doesn't work how can i fix it?
View
<%= link_to (tag.title), articles_path(:scope => tag.title) %>
Model(Article)
def self.by_tags(tag)
joins(:tags).where('tags.title = ?', tag)
end
Controller
def index
#articles = Article.by_tags(params[:scope])
end
I am trying to create a dashboard where users (User model) who has clicked "attending" (which is a flaggable) to an event (Event model) which is connected to a Collection (Collection model) will be able to see what are the Events they are going for.
My question however, is simply how to loop through all possible arrays in order for me to get all the associated IDs for the Events that the User has clicked "attending".
So for example, in my home page I want to display all possible events that the user is attending:
user_statistics.html.erb
<div class="span3 events">
<h3>Events</h3>
<% if #events.empty? %>
<p>You are currently not attending any events.</p>
<% else %>
<p>You are attending: <b><%= pluralize(#events.count, "event") %></b></p>
<p>Event 1: <%= #event1_name %> on Date: <%= #event1.date %> at Time:<%= #event1.time %></p>
<% end %>
</div>
pages_controller.rb
def home
#title = "Title"
#user = current_user
if current_user
#post = Post.new
#feed_items = current_user.feed
#user_following = #user.following
#user_followers = #user.followers
#events = #user.flaggings.with_flag(:attending)
#event1 = Event.find(#events[0].flaggable_id)
#event1_name = Collection.find(#event1.collection_id).name
end
end
I have set the #event1 array to 0 to access the first flag for 'attending', and then get the flaggable_id so I have the id to call up the Collection.
My issue is that if I have several events, how do I go about looping through to all the arrays to ensure I can pull out all the Collections?
For the first User who has clicked "attending" for 2 events, this is the data:
in IRB, for User.first who is attending 2 events
User Load (0.3ms) SELECT "users".* FROM "users" LIMIT 1
MakeFlaggable::Flagging Load (0.4ms) SELECT "flaggings".* FROM "flaggings" WHERE
"flaggings"."flagger_id" = 1 AND "flaggings"."flagger_type" = 'User' AND
"flaggings"."flag" = 'attending'
[#<MakeFlaggable::Flagging id: 16, flaggable_type: "Event", flaggable_id: 3,
flagger_type: "User", flagger_id: 1, flag: "attending", created_at: "2012-02-20 09:26:36",
updated_at: "2012-02-20 09:26:36">, #<MakeFlaggable::Flagging id: 18, flaggable_type:
"Event", flaggable_id: 4, flagger_type: "User", flagger_id: 1, flag: "attending",
created_at: "2012-02-20 10:38:00", updated_at: "2012-02-20 10:38:00">]
You can see that the user has flagged 'attending' for 2 events, which are stored in arrays. Hence if I have 2 events, I would ideally want to loop through such that I have 2 arrays.
I find it quite confusing to implement this... any help would be much appreciated!
user.rb partial code
Class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :user_bio,
:shop, :cover_photo, :avatar, :remote_image_url
has_secure_password
mount_uploader :cover_photo, ImageUploader
mount_uploader :avatar, ImageUploader
make_flagger
scope :shop, where(shop: true)
has_many :posts, dependent: :destroy
has_many :relationships, dependent: :destroy,
foreign_key: "follower_id"
has_many :reverse_relationships, dependent: :destroy,
foreign_key: "followed_id",
class_name: "Relationship"
has_many :following, through: :relationships, source: :followed
has_many :followers, through: :reverse_relationships, source: :follower
has_many :collections, dependent: :destroy
...
end
collections.rb partial code
class Collection < ActiveRecord::Base
attr_accessible :name, :description, :image, :remote_image_url
belongs_to :user
has_many :products, dependent: :destroy
has_many :events, dependent: :destroy
mount_uploader :image, ImageUploader
make_flaggable :like
...
end
events.rb partial code
class Event < ActiveRecord::Base
attr_accessible :date, :time, :description
belongs_to :collections
make_flaggable :attending
...
end
It's not 100% clear from your post, but it sounds like you need a named scope for Events where the attending flag is set and a has_many :through association to let the User have Events. With those two bits together, you could do something like:
User.first.events.attending
and AREL will take care of wiring that all up into a nice fast query for you.
Is flaggable a gem or have you written it as a polymorphic class?
This line is kind of nasty (sorry):
#events = #user.flaggings.with_flag(:attending)
The #events variable isn't holding events - it's holding a collection of flaggings. As a naming convention, this is obviously bad.
Also, this line is probably redundant if you've set up (or used a gem) for the flaggable polymorphic relationship:
#event1 = Event.find(#events[0].flaggable_id)
#This could be rewritten as:
#event1 = #events.first.flaggable
Or even nicer, combine the two previous lines into:
##events = #user.flaggings.with_flag(:attending)
##event1 = Event.find(#events[0].flaggable_id)
#becomes:
flaggings = #user.flaggings.with_flag(:attending)
#events = flaggings.map(&:flaggable)
#jxpx777 makes a very good point about named scope and hmt associations. This is probably the way forward. You could do something like this:
Class User < AR...
has_many :attendances, :class => 'Flagging', :conditions => {:flag => 'attending'} #You might need to add :type => "Event" if you use flaggings elsewhere...
has_many :events_attending, :class => 'Event', :through => :attendances, :source => :flaggable_id #You'll probably have to change a lot of these variable names - I can't see the rest of your source code...
That will then give you ability to just do:
#events = #user.events_attending.include(:collection)
Caveat - none of this code is tested - it's all off the top of my head, but it should point you in the right direction at least
Okay, I managed to solve my own problem albeit in a very dirty manner...
pages_controller.rb
def home
#title = "Simplifying and socializing online shopping - Ruuva"
#user = current_user
if current_user
#post = Post.new
#feed_items = current_user.feed
#user_following = #user.following
#user_followers = #user.followers
#events_attending = #user.flaggings.with_flag(:attending)
end
end
_user_statistics.html.erb partial
<h3>Events</h3>
<% if #events_attending.empty? %>
<p>You are currently not attending any events.</p>
<% else %>
<p>You are attending these events:</p>
<ol>
<% #events_attending.each do |e| %>
<% unless Event.find_by_id(e.flaggable_id).nil? %>
<li>
Collection: <%= link_to Collection.find(Event.find(e.flaggable_id).collection_id).name, event_path(e.flaggable_id) %>
<br>happening on: <%= Event.find(e.flaggable_id).date %> at <%= Event.find(e.flaggable_id).time %>
</li>
<% end %>
<% end %>
</ol>
<% end %>
I'm pretty sure the code is very bad, and I DO used several 'shortcuts' issues that will bite me back in the future... but an mvp is an mvp. Thanks guys for the help, though! :)
I have three tables: users, members, projects. The middle is a join table expressing a has-many-through between the other two tables; and it has some attributes of interest, including join_code and activated.
More expansively:
class User < ActiveRecord::Base
has_many :members
has_many :projects, :through => :members
end
class Member < ActiveRecord::Base
belongs_to :user
belongs_to :project
# has a column called join_code
# has a column called activated
# Note that this class can be thought of as "membership"
end
class Project < ActiveRecord::Base
has_many :members
has_many :users, :through => :members
end
Goal: Given a particular user, I want a query that will get all the projects, and eager load only the member records that link those projects to the user.
So far I have this method in user.rb that does a query:
def live_projects
self.projects.order("projects.name").includes(:members).where(:members => {:join_code => nil, :activated => true})
end
But it's not enough. I'd like to then be able to do this in the view code:
<% current_user.live_projects.each do |project| %>
<li project_id="<%= project.id %>">
<% member = project.member %>
(Do something with that member record here)
<%= project.name %>
<% end %>
</li>
<% end %>
Here, normally, I'd have project.members, but in my context I'm only interested in that one member record that links back to the user.
Here is what I think the raw SQL should look like
select projects.*, members.*
from projects inner join members on projects.id = members.project_id
where members.user_id = X and members.join_code is null and members.activated = 't';
How to do that in Arel (or ActiveRecord)?
I may have something of an answer here, namely that the ActiveRecord code I wrote seems pretty reasonable. Again, here's that query:
def live_projects
self.projects.order("projects.name").includes(:members).where(:members => {:join_code => nil, :activated => true})
end
On a run through the UI with sample data it generates this output from Rails server:
Project Load (0.6ms) SELECT "projects".* FROM "projects" INNER JOIN "members" ON "projects".id = "members".project_id WHERE "members"."join_code" IS NULL AND "members"."activated" = 't' AND (("members".user_id = 3)) ORDER BY projects.name
Member Load (2.0ms) SELECT "members".* FROM "members" WHERE ("members".project_id IN (50,3,6,37,5,1))
Then later in the view code I can do this:
<% current_user.live_projects.each do |project| %>
<li project_id="<%= project.id %>" class="<%= 'active' if project == #project %>">
<% member = project.members.detect { |member| member.user_id == current_user.id } %>
(Do something with that member record here)
</li>
<% end %>
That expression to get the member record is pretty ugly in the view, but select is an Array method, not a query, and no extra DB hits other than the two shown above appear in the output from Rails server. Thus I guess my n+1 problem is solved.
Add an association called live_members on the Project class.
class Project < ActiveRecord::Base
has_many :live_members, :class_name => "Member",
:conditions => {:join_code => nil, :activated => true}
has_many :members
has_many :users, :through => :members
end
Add an association called live_projects on the User class.
class User < ActiveRecord::Base
has_many :members
has_many :projects, :through => :members
has_many :live_projects, :through => :members, :source => :project,
:include => :live_member, :order => "projects.name"
end
Now you can:
user.live_projects
It seems that you expect there to be at most one active member linking each user to a project. If this is the case the following should work:
In member.rb:
scope :live, where(:join_code => nil, :activated => true)
In user.rb:
def live_projects_with_members
members.live.includes(:project).group_by(&:project)
end
In your view:
<% current_user.live_projects_with_members.each do |project, members| %>
<% member = members.first %>
<li project_id="<%= project.id %>" class="<%= 'active' if project == #project %>">
(Do something with that member record here)
</li>
<% end %>
If you then want to add an extra join for your usage stats you can do this:
def live_projects_with_members
members.live.includes(:project, :stats).group_by(&:project)
end
I've got a question about associations in Rails 2.3.11
I have two models:
Activities
Organisations
they have a has_and_belongs_to_many relationship. But they also have an important and per-activity-organisation relationship type. They are connected together via the activities_organisations table, which has three columns:
activity_id
organisation_id
rel_type
The rel_type can be set as 1 (funding), 2 (extending) or 3 (implementing).
The association works fine and I can call #activity.organisations or #organisation.activities. But I'd also like to call this depending on the rel_type - so that I can for example call only an activity's implementing organisation (where rel_type == 3)
At the moment I have this (on the Organisations / index view), but I think there must be a nicer way?
<%
#conditions = {}
#conditions[:organisation_id] = organisation.id
#reltype = ActivitiesOrganisation.all(:conditions=>#conditions, :group=>:rel_type)%>
<td><% #reltype.each do |r| %><%= r.rel_type %><% end %></td>
</tr>
<% end %>
<% end %>
I found this but I got stuck trying to include it in my models, and I'm not sure how much of it applies.
Would be extremely grateful for any help!
Thanks,
Mark
Update:
Thanks to #ghoppe in the comments, I realised I was being a bit of an idiot... This is my code now:
Activity Model
class Activity < ActiveRecord::Base
has_many :activity_organisations
has_many :funding_activity_organisations, :class_name => "ActivitiesOrganisation",
:conditions => {:rel_type => 1}
has_many :extending_activity_organisations, :class_name => "ActivitiesOrganisation",
:conditions => {:rel_type => 2}
has_many :implementing_activity_organisations, :class_name => "ActivitiesOrganisation",
:conditions => {:rel_type => 3}
has_many :organisations, :through => :activity_organisations
has_many :funding_organisations, :through => :funding_activity_organisations, :source => :organisation
has_many :extending_organisations,:through => :extending_activity_organisations,:source => :organisation
has_many :implementing_organisations,:through => :implementing_activity_organisations,:source => :organisation
end
ActivitiesOrganisation Model
class ActivitiesOrganisation < ActiveRecord::Base
belongs_to :activity
belongs_to :organisation
end
Thanks!
I have a model called Kase each "Case" is assigned to a contact person via the following code:
class Kase < ActiveRecord::Base
validates_presence_of :jobno
has_many :notes, :order => "created_at DESC"
belongs_to :company # foreign key: company_id
belongs_to :person # foreign key in join table
belongs_to :surveyor,
:class_name => "Company",
:foreign_key => "appointedsurveyor_id"
belongs_to :surveyorperson,
:class_name => "Person",
:foreign_key => "surveyorperson_id"
I was wondering if it is possible to list on the contacts page all of the kases that that person is associated with.
I assume I need to use the find command within the Person model? Maybe something like the following?
def index
#kases = Person.Kase.find(:person_id)
or am I completely misunderstanding everything again?
Thanks,
Danny
EDIT:
If I use:
#kases= #person.kases
I can successfully do the following:
<% if #person.kases.empty? %>
No Cases Found
<% end %>
<% if #person.kases %>
This person has a case assigned to them
<% end %>
but how do I output the "jobref" field from the kase table for each record found?
Maybe following will work:
#person.kase.conditions({:person_id => some_id})
where some_id is an integer value.
Edit
you have association so you can use directly as follows:
#kases= #person.kases
in your show.rhtml you can use instance variable directly in your .rhtml
also #kases is an array so you have to iterate over it.
<% if #kases.blank? %>
No Kase found.
<% else %>
<% for kase in #kases %>
<%=h kase.jobref %>
<% end %>
<% end %>
If your person model has the association has_many :kases then, you can get all the cases that belongs to a person using this
#kases = Person.find(person_id).kases
Assuming person_id has the id of the person that you want to see the cases for.
You would probably want something like
has_many :kases
in your Person model, which lets you do #kases = Person.find(person_id).kases
as well as everything else that has_many enables.
An alternate method would be to go through Kase directly:
#kases = Kase.find_all_by_person_id(person_id)