How to access a model from another model? - ruby-on-rails

I have two models in ROR, one which is Note and another one which is Access. Each Access has a Note field and a user field. In my index action of the notes controller I want to filter notes owned by the user (done) as well as notes accessible to the user, which I named #accessible_notes.
The following code gives me the correct notes owned by the user, however I cannot get the notes accessible to the user.
Basically, I need to find all the Accesses in which the user is involved and then fetch the corresponding notes. How can I do that?
def index
#notes = Note.where(user: current_user)
#personal_access = Access.where("user_id = ?",current_user.id)
#accessible_notes = []
#personal_access.each do |accessible|
tnote = Note.find(accessible.note_id)
#accessible_notes += tnote if tnote
end
end

class User < ActiveRecord::Base
has_many :accessible_notes, :through => :accesses, :source => :notes
end
#accessible_notes = current_user.accessible_notes

How about
#personal_access.each do |accessible|
#accessible_notes << accessible.note
end
#accessible_notes.flatten!
There might be a faster way using Active Record queries.
And that faster way is in depa's answer.

Related

Which rails model do I put a multiple table query in

I have two tables called
Product (prodID: integer, prodName: string, userID: FK)
and
User(userID:integer,userName:string).
The user can have many products. I want to write a query that gets me all the products for userID=10. I don't however understand which model I should put this in- the user or the product model or does it not matter? Presumably the output of the model will be fed to the controller it relies on so I should put it in the model that relates to the view I want to show it in? Is this correct?
You can directly use association method, no need of writing model method for fetching user's products.
In user.rb:
has_many :products
In product.rb
belongs_to :user
and from controller
User.where('id = ?', params[:id]).first.try(:products)
So, above query will fetch products if user with given id is found.
In your controller:
#user = User.find(params[:id])
#products = User.of_products(params[:id])
If you don't want to use #user in your action then you can avoid calculating #user.
In user.rb:
has_many :products
def self.of_products(user_id)
User.includes(:products).where(id: user_id)
end
This will give you all products of #user

Create variable that can be defined and accessed in multiple controllers in Rails

I'm creating an app for creating multiple to-do lists. So a user signs in, has multiple lists, and each list contains multiple items. Everything else is working, but I'm struggling to create the items.
My code for creating a new item on a list (this is found in the items_controller) is :
def create
#list =
#new_item = #list.items.build(params[:item])
if #new_item.save
flash[:success] = "Item saved!"
end
redirect_to root_path
end
And the issue is, I'm not sure how to define what #list should be. I have a variable current_user (based on the session) for creating a new list, but there is only one user per session and multiple lists per session, so I can't just replicate that method.
Basically, I'm stuck on how to be able to have the item know which list it belongs to (which should be the list whose show page I was just on). In Java I'd have a static variable that I would redefine every time I went to a list, but I tried doing that and it didn't work, and I read that apparently in rails that doesn't work.
Rather than storing a variable, you should keep track of the list using url parameters. You will need to modify your form to include a list_id parameter. Then in the controller when you are creating a list item, do something like:
#list = List.find params[:list_id]
#new_item = #list.items.build params[:item]
Or if you made your params include an items[list_id] parameter, then in Rails it will be accessible in params[:item][:list_id] so you should just be able to just do:
#new_item = ListItem.create params[:item]
If you do it this second way, just be sure to add a validation in the ListItem model to guarantee that list_id is present and the list it points to exists.
The listItem should have a foreign key to the list model, and the list model should have a foreign key to the account. In this way you should be able to traverse the structure easily.
class ListItem < ActiveRecord::Base
belongs_to :list
end
class List < < ActiveRecord::Base
belongs_to :account
has_many :list_items
end
class Account < < ActiveRecord::Base
has_many :lists
end

Don't reshow seen posts in Rails

I'm currently developing an application whereby a user clicks a button and they're offered up a new page of content, and was wondering how I would go about hiding or skipping past those that the user has already interacted with (a separate table stores the post_id and user_id for each view).
I currently use this code in the model for displaying a random page:
def self.random
if (c = count) != 0
find(:first, :offset =>rand(c))
end
end
The user authentication system is built off of Authlogic, and I have User, Post and View models.
So if a user has already seen a post "foo", how would I not display that in the future and instead serve up a random "bar".
Thanks
Steve,
I would set a boolean field for each post called "read" (default => false).
Upon firing the "show" action of your controller (and any other action you consider the person seeing the post), you can automatically set that to true and perform a save without validation. When you then show your list of records, you can add the condition .where("read = ?", false).
Of course, you can decide whether you want to give users the flexibility of setting individual posts to 'unseen' or 'unread' - if you want to do that it's the subject of another question :).
You could store an array of viewed post ids on the session in the show action of the posts_controller. EDIT -- find random post not already viewed. Not tested, but the idea is here:
def show_random_post
while (id == nil || (session[:viewed_posts] ||= []).include?(id)) # initialize array if it hasn't been initialized
id = rand(Post.count) + 1
end
session[:viewed_posts] << id
#post = Post.find(id)
# etc.
end
Do you want to keep a record of viewed posts between sessions?
EDIT: If you want to keep a user-level record of viewed posts between sessions, you'll probably want to do it at the db level. Since this means a many-to-many relationship between users and posts, you'll likely want to manage that with a relational table, and the best way to do that in Rails is with has_many :through. Something like (again, not tested):
class ViewedPostRecord < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
class User < ActiveRecord::Base
has_many :viewed_post_records
has_many :viewed_posts, :class => 'Post', :through => :viewed_post_records
end
class PostsController < ApplicationController
def show_random_post
while (id == nil || current_user.viewed_posts.map(&:id).include?(id))
id = rand(Post.count) + 1
end
#post = Post.find(id)
current_user.viewed_posts << #post
# etc.
end
end

nested form & habtm

I am trying to save to a join table in a habtm relationship, but I am having problems.
From my view, I pass in a group id with:
<%= link_to "Create New User", new_user_url(:group => 1) %>
# User model (user.rb)
class User < ActiveRecord::Base
has_and_belongs_to_many :user_groups
accepts_nested_attributes_for :user_groups
end
# UserGroups model (user_groups.rb)
class UserGroup < ActiveRecord::Base
has_and_belongs_to_many :users
end
# users_controller.rb
def new
#user = User.new(:user_group_ids => params[:group])
end
in the new user view, i have access to the User.user_groups object, however when i submit the form, not only does it not save into my join table (user_groups_users), but the object is no longer there. all the other objects & attributes of my User object are persistent except for the user group.
i just started learning rails, so maybe i am missing something conceptually here, but i have been really struggling with this.
Instead of using accepts_nested_attributes_for, have you considered just adding the user to the group in your controller? That way you don't need to pass user_group_id back and forth.
In users_controller.rb:
def create
#user = User.new params[:user]
#user.user_groups << UserGroup.find(group_id_you_wanted)
end
This way you'll also stop people from doctoring the form and adding themselves to whichever group they wanted.
What does your create method look like in users_controller.rb?
If you're using the fields_for construct in your view, for example:
<% user_form.fields_for :user_groups do |user_groups_form| %>
You should be able to just pass the params[:user] (or whatever it is) to User.new() and it will handle the nested attributes.
Expanding on #jimworm 's answer:
groups_hash = params[:user].delete(:groups_attributes)
group_ids = groups_hash.values.select{|h|h["_destroy"]=="false"}.collect{|h|h["group_id"]}
That way, you've yanked the hash out of the params hash and collected the ids only. Now you can save the user separately, like:
#user.update_attributes(params[:user])
and add/remove his group ids separately in one line:
# The next line will add or remove items associated with those IDs as needed
# (part of the habtm parcel)
#user.group_ids = group_ids

How do I only show items that belong to a certain user (using restful_authentication)?

I have a web application with users and their documents. Each user can have many documents:
user.rb:
has_many :documents
document.rb:
belongs_to :user
document_controller.rb:
def index
#documents = Document.find(:all)
end
I am using the restful_authentication plugin. Here is my question: How do I get the controller to only show documents that belongs to each user? Right now it shows all the documents for all the users.
I am using the latest version of Rails.
You set a relationship in your User class to your Document class. This will automatically add a method to your User objects that returns a list of all documents related to a particular user:
def index
#documents = #current_user.documents
end
See the documentation for other automatically added methods.
Try this:
def index
#documents = Document.where(:user_id => current_user.id)
end
def index
#documents = Document.find(:all, :conditions => {:user_id => session[:user_id]})
end
Take a look here in the rails API in the Association Join Models section.
However be aware Restful authentication won't control access in order to limit the users to only their own records particularly with restful routes. They can still view other users' records by entering values in the urls once they are logged in.
For that you might want to look into Restful ACL

Resources