I have a pretty complicated set of models and I'm trying to figure out how to set it all up so that rails will generate chained queries (though I'm not sure it is possible). So my question ends up being: how do I chain these together OR how do I properly paginate this type of function?
First, the high overview.
It is a "social network" and I have users who can be students or teachers. Students and teachers can both follow each other. Both can also do things on the site that result in activity_items. A students can belong to a teacher. Therefore, if a student is following a teacher, they should also see the activity_items of the students under that teacher.
And that is where it gets complicated. I am trying to figure out how to display an activity feed.
Now the models:
model ActivityItem < ActiveRecord::Base
belongs_to :profile, :polymorphic => :true
attr_accessor :posted_by_user
end
model Following < ActiveRecord::Base
belongs_to :profile, :polymorphic => true
belongs_to :following, :polymorphic => true
end
model Student < ActiveRecord::Base
has_many :following_relationships, :as => :profile, :class_name => "Following"
has_many :follower_relationships, :as => :following, :class_name => "Following"
# getting the actual student/teacher model for each relationship
def following
self.following_relationships.reset
following_relationships.collect(&:following)
end
def activity_feed
all = following
all.inject([]) do |result,profile|
result | profile.activity_items
end
end
end
model Teacher < ActiveRecord::Base
[same as student, really it is in an Extension]
end
So the method I'm most concerned about is activity_feed. Without running the large set of queries every time, I'm not sure how to paginate the data. I have a few more methods that go through each activity_item and set posted_by_user to true or false to let me know if it is an activity_item created by someone they are following directly or indirectly (through the teacher).
How can I modify this so that I'm able to either
(A) get a single (or two or three) queries so that I can do a pagination method on it.
(B) how would I properly cache / paginate a large data set like this?
Related
In my Rails 4 app, I have the following models:
class Person < ActiveRecord::Base
has_many :addresses
end
class Address < ActiveRecord::Base
belongs_to :person
belongs_to :city
end
class City < ActiveRecord::Base
has_many :addresses
end
I'm using the :includes function to return query result into one variable:
Address.includes(:person, :city).where("person_id = 1")
It works as expected, except that I do not want the query to return every single column.
Here's what I've tried:
use select and specify table name and column names explicitly, e.g. "city.name", but Rails generates a big query with outer joins, that can be very costly, especially when there are lots of concurrent requests, so prefer a better solution.
don't want to hard code complete and raw SQL statements, because of maintenance issue later on
create a new "dummy" belongs_to relationship like in Address: belongs_to :city_select_columns, -> { select('name') }, :class => 'City', but that doesn't work (actually I'm not sure if that select is even supported, only came across documentation about where so far).
maybe define scope in City and Person? but I'm not sure how it should be defined or if it'd make sense to do it this way
Suggestions? Thanks
Have you tried this?
class Person < ActiveRecord::Base
has_many :addresses
has_many :cities, :through => :addresses
end
class Address < ActiveRecord::Base
belongs_to :person
belongs_to :city
end
class City < ActiveRecord::Base
has_many :addresses
end
Then:
Person.find(1).cities.pluck(:name)
Looks like this generates an INNER JOIN but with indexes it shouldn't be too costly?
Did you try select?
Address.select(<output_columns>).includes(:person, :city).where("person_id = 1")
Could not find a good query method using Rails' API, I ended up writing a raw inner join SQL, then call ActiveRecord::Base.connection.execute to run it.
I have two models to make a relationship between them, where I need to access stores of a radar and the radars of a store. A radar could monitoring zero or many stores. A store could belong to zero, one or many radars.
I would like to have something like this:
store = Store.first
store.radars #all radars of the store location
And the opposite too:
radar = Radar.first
radar.stores #all stores of the radar location
My classes:
class Store < ActiveRecord::Base
attr_accessible :title, :description, :user, :store_group, :city,
:neighborhood, :sublocality, :post_code, :route,
:street_number, :latitude, :longitude
end
class Radar < ActiveRecord::Base
attr_accessible :name, :radius, :latitude, :longitude, :user
end
How can I create a migration to handle this?
What you are looking for is a has_and_belongs_to_many association between radars and stores. The question you need to ask your self is will there ever be any attributes on the the joining between the two models? If so you might considering using an explicit join model, that will hold those attributes. In that case you would be looking at a has_many :through association.
see http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association for information on the HABTM association.
your migration for a HABTM would be something like this.
class CreateRadarStores < ActiveRecord::Migration
create_table :radars_stores, :id => false do |t|
t.belongs_to :radar
t.belongs_to :store
end
end
The order of the table name is important, since by default rails creates it in alphabetical order of the models.
Your models would need to be updated to include the HABTM
class Store < ActiveRecord::Base
has_and_belongs_to_many :radars
....
end
class Radar < ActiveRecord::Base
has_and_belongs_to_many :stores
....
end
or if using a has many :through look here http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
Building that join model would be up to you depending upon attributes reuqired.
I have a stores application on github. I'm trying to implement counter_cache for two models, 1. Divisions model and 2. Products model. For some reason I'm not sure of the counter cache(divisions_count) doesn't get incremented automatically for the Company model whenever I create a new Division and similarly the products_count doesn't get incremented for the Divisions model when I add a new product to the division.
I'm on rails 3.2.11 and on ruby 1.9.3-p327
My application is only at POC level.
PFB the model structure wrt Company, Division and Product:-
company.rb
class Company < ActiveRecord::Base
attr_accessible :contact_no, :email_id, :fax_no, :name, :website, :divisions_count
has_many :divisions #just added divisions_count to attr_accessible not sure if it helps
has_many :products, :through => :divisions
end
division.rb
class Division < ActiveRecord::Base
attr_accessible :company_id, :name, :products_count
#just added products_count to attr_accessible not sure if it helps
belongs_to :companies, :counter_cache => true
has_many :products
end
product.rb
class Product < ActiveRecord::Base
attr_accessible :division_id, :model, :name, :price
belongs_to :divisions, :counter_cache => true
end
In case you want to refer to the migrations that I've created for the counter cache implementation , you may find them here.
I believe the problem is that you’ve set up belongs_to incorrectly by using the plural name. Switching to singular solves the problem, i.e.
# pseudo diff:
- belongs_to :companies, :counter_cache => true
+ belongs_to :company, :counter_cache => true
- belongs_to :divisions, :counter_cache => true
+ belongs_to :division, :counter_cache => true
When editing models/associations I found it helpful to think of an actual instance. So, it makes sense that the “Windows”-division belongs to “Microsoft”-company, but it makes no sense that it belongs to “Microsoft”-companies. Or just remember belongs_to is always singular and has_many is always plural.
If you need your divisions to belong to multiple companies, you need to use a different association called “has and belongs to many” or HABTM for short (see [1]).
[1] http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
I have a many to many :through relationship between a set of classes like so:
class Company
has_many :shares
has_many :users, :through => :shares, :uniq => true
end
class User
has_many :shares
has_many :companys, :through => :shares, uniq => true
end
class Share
belongs_to :company
belongs_to :user
end
I want to ensure a unique relationship so that a user can only have one share in any one company, which is what I have tried to achieve using the "uniq" argument.
At first I thought this was working, however it seems the behaviour os the "uniq" is to filter on the SELECT of the record, not pre-INSERT so I still get duplicate records in the database, which becomes an issue if I want to start dealing with the :shares association directly, as calling user.shares will return duplicate records if they exist.
Can anyone help with an approach which would force truely uniq relationships? so that if I try adding the second relationships between a user and a company it will reject it and only keep the original?
Have you tried adding this to your Share class?
validates_uniqueness_of :user, scope: :company
Also, in your User class I think it should be:
has_many :companies, through: :shares
I hope that helps.
I'm pretty new to Rails, and i'm trying to do a polymorphic HABTM relationship. The problem is that I have three models that I want to relate.
The first one is the Event model and then are two kind of attendees: Users and Contacts.
What I want to do is to be able to relate as an attendee both users and contacts. So, what i have right now in my code is:
Event Model
has_and_belongs_to_many :attendees, :polymorphic => true
User Model
has_and_belongs_to_many :events, :as => :attendees
Contact Model
has_and_belongs_to_may :events, :as => :attendees
How the HABTM table migration needs to be? I'm a little confused and i have found no help on that.
Is it going to work?
No, you can't do that, there's no such thing as a polymorphic has_and_belongs_to_many association.
What you can do is create a middle model. It would probably be something like this:
class Subscription < ActiveRecord::Base
belongs_to :attendee, :polymorphic => true
belongs_to :event
end
class Event < ActiveRecord::Base
has_many :subscriptions
end
class User < ActiveRecord::Base
has_many :subscriptions, :as => :attendee
has_many :events, :through => :subscriptions
end
class Contact < ActiveRecord::Base
has_many :subscriptions, :as => :attendee
has_many :events, :through => :subscriptions
end
This way the Subscription model behaves like the link table in a N:N relationship but allows you to have the polymorphic behavior to the Event.
Resolveu parcialmente.
It does solve the problem given the framework that we have at our disposal, but it adds "unnecessary" complexity and code. By creating an intermediary model (which I will call B), and given A -> B -> C being "A has_many B's which has_many C's", we have another AR Model which will load one more AR class implementation into memory once it is loaded, and will instantiate for the sole purpose of reaching C instances. You can always say, if you use the :through association, you don't load the B association, but then you'll be left with an even more obsolete model, which will only be there to see the caravan pass by.
In fact, this might be a feature that is missing from Active Record. I would propose it as a feature to add, since it has been cause of concern for myself (that's how I landed in this post hoping to find a solution :) ).
Cumprimentos