Ruby on Rails - How to Query on model/condition on controller? - ruby-on-rails

I'm using rails 3.2.3 and have a questions about queries.
I've read that it is favorable using arel instead of named scopes.
Basically in my app, when a user logs in, I want him to see the products that he created.
So instead of having in my controllers index:
products=Product.find(:all)
I was looking for something like
products=Product.find(:all, :conditions....)
The thing is, my User and Product models have a HABTM relation (it really has to be) and I don't know how to join those tables so that only the products inserted by the current_user are displayed (the insertion is working correctly)
Do I have to add a search method in my Product model?
Or this can be accomplished by passing :conditions in the index controller?
Basically the logic is:
->Get all the products
->inner joining with the products_users HABTM table and get all the products where products_users.user_id = current_user.id. Return the results.
I don't know if I'm missing something here...any tips on how to code this? I'm kind of confused.

if user_sighed_in?
#products = current_user.products
else
#products = Product.scoped
end
ofc u have to define association in User model
has_many :products

If you have associated User and Products models - this code #products = current_user.products will return products of current_user.

To find all the products of current user this will do the trick
current_user.products

Related

Get all active records WHERE param array contains a specific value

In my rails controller I'd like to retrieve only the Projects where the current user's id is in a param array.
This is what I currently have:
class MyProjectsController < ApplicationController
before_action :authenticate_user!
def index
#user = current_user
#projects = Project.where(team_member: [#user])
end
private
def project_params
params.require(:project).permit(:name, :team_member => [])
end
end
My expectation was that this would return projects if #user was included in the array...
It actually only returns projects if #user is the only value in the array. If the array contains multiple values nothing is returned.
I'm fairly new to rails and feel like I'm missing something really obvious here.
Thanks in advance!
Update:
So I was adding Users to a Project via a column on the Projects table called :team_member which accepts an array of user IDs.
I did get this to work by doing a query like this:
#projects = Project.where(":team_member = ANY(team_member)", team_member:
[current_user.id])
This returns only projects where current_user.id exists in the :team_member array.
BUT I realize that I really need to perhaps do this with a join table on User and Projects and add users to projects that way. Thanks for the comments...they all helped me think through this.
We could help better you show us the Project model. Specifically the association between Project and team_member.
If team_member is a has_many association, you could join it and filter by their ids, like:
Project.joins(:team_members).where(team_members: { id: YOUR_ARRAY_OF_IDS })
But maybe team_member is an array, or some sort of data serialized in Project model, for that we could search array elements using ANY or ALL, using postgresql, I can't tell much about other DBMS.
Please tell more about this model and it's associations
i would suggest you to use either has_any_belongs_to_many relationship , or has_many :through for essay querying database
https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
https://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
Try this:
#projects = Project.all.where(team_member: current_user)

How does one actually use a has_many/belongs_to relationship?

My two models are User has_many :books and Book belongs_to :user.
The users table has only the column name, while the books table has users_id and title.
Is this how I'm actually supposed to use them? With the users table populated, how do I go about adding a book with a specific user, done by searching their name and not the ID column? I know this is a simple thing, but I really cannot find it on Google, or by re-reading my books and re-watching my Lynda videos; I know the information must be in there somewhere but it is completely frying my brain right now, and I'm extremely confused. I'm very used to SQL and learning to use ActiveRecord instead feels like trying to write with my left hand.
What I want to do is the equivalent of, in SQL, INSERT INTO books (title, users_id) VALUES ("Wolves of the Calla", (SELECT id FROM users WHERE name = 'Sarah'));.
Find the user with the given name and then use the association to create a book with the found user_id
user = User.where(:name => "Sarah").first
user.books.create(:title => "Wolves of the Calla")
As explained in the Association Basics Guide, you'd need something like this:
createdBook = #user.books.create(title: "Wolves of the Calla")
This can all be found in the Rails Documentation. Its worth a read, if you haven't done so already.
In regards to this question:
...how do I go about adding a book with a specific user...
There are a number of ways you can do it, but the key thing to remember is that the has_many and belongs_to methods "create" association methods for you. In this way you can retrieve, and add to an object's associations; Rails will take care of handling the foreign keys and such, so long as they are named in accordance with its naming convention (which it seems you have done)
So as a simple example, to add a book to a user's collection of books, would be something like this:
#user = User.where(name: "Sarah").first
#user.books.create(title: "Wolves of the Calla")
#Rails 4 syntax
#Approach 1
#user = User.find_by(name: 'Sarah')
#book = #user.books.create(title: 'Wolves of the Calla')
#Alternatively
#user = User.find_by(name: 'Sarah')
#user.books << Book.create(title: 'Wolves of the Calla')
#Alternatively
#user = User.find_by(name: 'Sarah')
#book = Book.create(title: 'Wolves of the Calla')
#user.book_ids += [#book.id]

Override just the default scope (specifically order) and nothing else in Rails

So basically I have two classes, Book and Author. Books can have multiple authors and authors can have multiple books. Books have the following default scope.
default_scope :order => "publish_at DESC"
On the Author show page I want to list all the books associated with that author so I say the following...
#author = Author.find(params[:id])
#books = #author.books
All is good so far. The author#show page lists all books belonging to that author ordered by publication date.
I'm also working on a gem that is able to sort by the popularity of a book.
#books = #author.books.sort_by_popularity
The problem is that whenever it tries to sort, the default_scope always gets in the way. And if I try to unscope it before it will get rid of the author relation and return every book in the database. For example
#books = #author.books.unscoped.sort_by_popularity # returns all books in database
I'm wondering if I can use the ActiveRelation except() method
to do something like this (which seems like it should work but it doesn't. It ignores order, just not when it is a default_scope order)
def sort_by_popularity
self.except(:order).do_some_joining_magic.order('popularity ASC')
# |------------| |---------------------|
end
Any ideas as to why this doesn't work? Any ideas on how to get this to work a different way? I know I can just get rid of the default_scope but I'm wondering if there another way to do this.
You should be able to use reorder to completely replace the existing ORDER BY:
reorder(*args)
Replaces any existing order defined on the relation with the specified order.
So something like this:
def self.sort_by_popularity
scoped.do_some_joining_magic.reorder('popularity ASC')
end
And I think you want to use a class method for that and scoped instead of self but I don't know the whole context so maybe I'm wrong.
I don't know why except doesn't work. The default_scope seems to get applied at the end (sort of) rather than the beginning but I haven't looked into it that much.
You can do it without losing default_scope or other ordering
#books.order_values.prepend 'popularity ASC'

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

Relation/Sort not working in rails controller?

I have the following relation in my rails app:
genre
- has many - authors
authors - belong to genre and has many books
books - belongs to authors and belongs to users
(users can add books to the db)
in my controller I have:
#books=current_user.books(:include => [:author => :genre], :order => 'created_at DESC')
While I am able to use the #books variable in my views - nothing is done correctly (i.e. its not showing me only books added by that user, and its not descending by created_at)...
any ideas?
--
Also I'm using clearance for the user auth, so current_user without the # in the controller seems to work fine
Actually, I think the relation is working, only the sort might not be working...
Which created_at do you want to sort on? When you're engaging a join, it's usually safer to be specific:
#books = current_user.books(..., :order => 'books.created_at DESC')
The other thing you can do is sort it in the client which may be faster than having the RDBMS do it if you're not paginating:
#books = current_user.books(...).sort_by(&:created_at).reverse
If you're paginating these results you will need to sort in the query, there's no way around it.

Resources