has_many by boolean value in rails - ruby-on-rails

In my ruby on rails app i have two models: User and Item
In my user model I would like a relation where a user can have many items like this:
has_many :items
For the relationship I would not only use the user_id column in the items table.
When I do
User.includes(:items)
I would rather have all items where
- the items user_id belongs to the user (the classical way)
- or e.g. a boolean column (like all_users) is true
Can this be done with
has_many :items + any special options?
And how would it look like?
Thanks & Regards, Andreas

User.includes(:items) just instructs rails SQL builder to load all nested items withing loaded users.
That said,
User.includes(:items).all # will load all users with all nested items
User.includes(:items).where(name: 'Joe') # will load user(s) Joe and their items
this is done to minimize an amount of queries against the database.
Whether one wants to load all items, the query on Item should be used, e.g.:
Item.where(user_id: User.find_by_name('Joe').pluck(:id))

Related

Explanation on Rails Associations - Rails 4

i'm new to rails and your help and advise would be much appreciated as i am finding this challenging
Aim: i want the creator of the event to be able to select more than one user as
hosts for a created event (just like how facebook allows the creator of
a page to be be able to select users as admins of a created page). Is the below how my model and schema should be displayed?
i was aiming to build something like this image. Event1 i can select Ian & Jesse as hosts, Event2 i can also select Ian again as a host and select Emma
This is how i imagine it so far to be built (your guidance would be much appreciated):
models
user.rb
has_many events
event.rb
belongs_to user
host.rb
belongs_to user
has_many events
schema
users
name
email
events
title
address
user_id
hosts
user_id
event_id
Started writing this as a comment but realised it was getting too wordy.
your model is broken ... an event has many users .. it doesn't belong_to a single user.
What you have is a many to many relationship between users and events which needs resolving through a join table (aka associative/junction table). You have gone some way to resolving this with the hosts table though this goes against the rails convention.
What you want is something like:
models
user.rb
has_and_belongs_to_many :events
event.rb
has_and_belongs_to_many :users
and create a join table that references the two models
users table
name
email
events table
title
address
events_hosts table
user_id
event_id
The rails convention is for the join table to be named by joining the two names of the tables it is joining lexically ordered - i.e. events before hosts, concatenated together to give events_hosts.
Alternatively, you can also create a join model if you prefer:
EventHost
belongs_to :user
belongs_to :event
and modify the has_and_belongs_to_many to has_many :event_hosts in the other two models - the database schema will remain the same.

Complex Rails Association

Not sure which association best fits what I want. Basically it's a todo list. The list is a task and that task has many users.
ie: Dig a hole and plant some seeds. Digging a hole will require two or more people. Jim and Jo are digging the hole and Jo will plant the seeds after. So two lists to complete, the first by two users and the other by one. Both user can complete both lists if needed.
Todo: has many lists
List: belongs to todo
List: has many users
User: has many lists
If Im not clear, each task (list) on a todo can be completed by any user. I struggle to see where to put a list_id on the users table. That's not possible as that user can be doing another (list) at the same time. Im not sure how through: :association comes into play here.
User.first.lists #= []
Todo.first.lists.first.users #= []
I get nothing as the user_id needs to go somewhere.
If I'm not mistaken it sounds like you need a join table. you then state that your records have a relation :through the join table.
Example
you have a join table called: user_lists which will contain 3 pieces of data (id, user_id, list_id)
so each time a user has a list you create a record on this table.
Then in your User model
class User < ActiveRecord::Base
has_many :lists, :through => :user_lists
end
If I have understood your setup correctly then I hope this helps, if not let me know.
You can read more about associations here http://guides.rubyonrails.org/association_basics.html

Issue with polymorphic ActiveRecord query

I have three models with the following associations:
User has_many :owns, has_many :owned_books, :through => :owns, source: :book
Book has_many :owns
Own belongs_to :user, :counter_cache => true, belongs_to :book
I also have a page that tracks the top users by owns with the following query:
User.all.order('owns_count desc').limit(25)
I would now like to add a new page which can track top users by owns as above, but with a condition:
Book.where(:publisher => "Publisher #1")
What would be the most efficient way to do this?
I'm interesting if there is something special for this case, but my shot would be the following.
First, I don't see how polymorphic association can be applied here. You have just one object (user) that book can belong to. As I understand, polymorphic is for connecting book to several dif. objects (e.g. to User, Library, Shelf, etc.) (edit - initial text of question mentioned polymorphic associations, now it doesn't)
Second, I don't believe there is a way to cache counters here, as long as "Publisher #1" is a varying input parameter, and not a set of few pre-defined and known publishers (few constants).
Third, I would assume that amount of books by single Publisher is relatively limited. So even if you have millions of books in your table, amount of books per publisher should be hundreds maximum.
Then you can first query for all Publisher's books ids, e.g.
book_ids = Book.where(:publisher => "Publisher #1").pluck(:id)
And then query in owns table for top users ids:
Owns.select("user_id, book_id, count(book_id) as total_owns").where(book_id: book_ids).group(:user_id).order(total_owns: :desc).limit(25)
Disclaimer - I didn't try the statement in rails console, as I don't have your objects defined. I'm basing on group call in ActiveRecord docs
Edit. In order to make things more efficient, you can try the following:
0) Just in case, ensure you have indexes on Owns table for both foreign keys.
1) Use pluck for the second query as well not to create Own objects, although should not be a big difference because of limit(25). Something like this:
users_ids = Owns.where(book_id: book_ids).group(:user_id).order("count(*) DESC").limit(25).pluck("user_id")
See this question for reference.
2) Load all result users in one subsequent query and not N queries for each user
top_users = User.where(:id => users_ids)
3) Try joining User table in the first order:
owns_res = Owns.includes(:user).select("user_id, book_id, count(book_id) as total_owns").where(book_id: book_ids).group(:user_id).order("total_owns DESC").limit(25)
And then use owns_res.first.user

ActiveAdmin automatically loading full association table

I'm working on a project that uses ActiveAdmin for its administration backend.
I have two models, a Book model which has_many Products. When I try to access the products index view in ActiveAdmin, it seems to try to load the full books table into memory (there are about 1.5 million books in my database). CPU usage goes up to 100% and memory usage spikes to gigabytes.
Turning on mysql logging confirms that this is what happens when this view is called:
17 Query SELECT `books`.* FROM `books`
As far as I can tell this happens before any attempt to load the products.
To figure out this issue I stripped the models down to their bare bones:
class Product < ActiveRecord::Base
belongs_to :book
end
class Book < ActiveRecord::Base
has_many :products
end
I also reduced the AA definition to its most basic form:
ActiveAdmin.register Product do
end
Is this normal for ActiveAdmin? It doesn't seem like desirable behavior.
For anyone dealing with this same issue, I finally traced it to the automatically generated sidebar in ActiveAdmin. This includes a search field that includes a select box for all associated records.
If you have an associated table with over a million records like I do AA will happily attempt to insert the entire table into the select box.
The answer was to include some custom filters in the AA definition for products like so:
ActiveAdmin.register Product do
filter :title
end
That way the association won't be included (unless you specify it yourself.)
A better approach now is to use remove_filter for the particular attribute or relationship:
Or you can also remove a filter and still preserve the default
filters:
preserve_default_filters!
remove_filter :id
https://activeadmin.info/3-index-pages.html

Ruby on rails activerecord joins - select fields from multiple tables

models:
#StatusMessage model
class StatusMessage < ActiveRecord::Base
belongs_to :users
default_scope :order => "created_at DESC"
end
#User Model
class User < ActiveRecord::Base
has_many :status_messages
end
In controller I want to join these two tables and get fields from both table. for example I want email field from User and status field from StatusMessage. When I use :
#status = User.joins(:status_messages)
Or
#status = User.includes(:status_messages)
It gives me only the user table data.
How can I implement this requirement?
You need to use includes here. It preloads data so you won't have another SQL query when you do #user.status_messages.
And yes you can't really see the effect: you need to check your logs.
First of all, I don't think it is possible (and reasonable) what you want to do. The reason for that is that the relation between User and StatusMessage is 1:n, that means that each user could have 0 to n status messages. How should these multitudes of attributes be included in your user model?
I think that the method joints in class ActiceRecord has a different meaning, it is part of the query interface. See the question LEFT OUTER joins in Rails 3
There are similar questions on the net, here is what I have found that matches most:
Ruby on Rails: How to join two tables: Includes (translated for your example) in the user a primary_status_message, which is then materialized in the query for the user. But it is held in one attribute, and to access the attributes of the status_message, you have to do something like that: #user.primary_status_message.status
When you use #status = User.includes(:status_messages) then rails eagerley loads the data of all the tables.
My point is when you use this User.includes(:status_messages) it will loads the data of status_messages also but shows only users table data then if you want first user status_messages then you have to #user.first.status_messages

Resources