Create an array from database rails - ruby-on-rails

i need help with the following topic:
I'm trying to create an array of users based on their ids
def show
#appointment = Appointment.where(:event_id => :id).all
#users = Array.new
#appointment.each do |appointment|
#users.fill(User.find(appointment.user_id))
end
end
First, im getting all appointments which event_id are the same to :id (which comes from the Event table) . Then i proceed to create an array to be filled later with users inside the .each do expression.
The problem is that #users is empty after the expresion ends.
What am i doing wrong?

Much simpler:
#users = User.find(Appointment.where(event_id: <id>).pluck(:id))
Your code does not work as you misunderstood what method fill does - it substitutes all elements of the array with passed object (pretty much like this, might take some extra params to alter its behaviour a little bit). You were most liekly looking for push or unshift methods.
Much better solution
If I am correct, your associations most likely looks like this:
Event has_many :appointments
Appointment belongs_to :user
n that case, you can simply create has_many :through association:
class Event < ActiveRecord::Base
has_many :appointments
has_many :users, through: :appointments
end
Then your query is just:
Event.find(<id>).users

#users = Appointment.includes(:user).where(:event_id => <id>).map(&:user)
should work, assuming you have the User has_many :appointments & Appointments belongs_to :user associations setup.

Related

Building an object through two associations in Rails

Is it possible to build an object through two has_many associations? For example:
# posts_controller.rb
def create
#post = current_account.posts.build(params[:post])
#post.author = current_user # I want to compact this line into the previous one
end
I did some research and found this:
#article = current_account.posts.build(params[:post], user_id: current_user.id)
However, that did not work. In console, I kept getting user_id: nil whenever I built a new object.
Another potential solution I could not implement:
#post = current_account.post_with_user(current_user).build(params[:post])
But every implementation of post_with_user I wrote failed.
My associations are as follows:
class Discussion < ActiveRecord::Base
belongs_to :account
belongs_to :author, class_name: 'User', foreign_key: 'user_id', inverse_of: :discussions
end
class User < ActiveRecord::Base
belongs_to :account
has_many :discussions, inverse_of: :author
end
class Account < ActiveRecord::Base
has_many :users, inverse_of: :account
has_many :discussions
end
The thing your code shows you trying to do, you should be able to do. It should look something like this:
#article = current_account.posts.build(params[:post])
Because you're building off of the list of the current account's posts, you don't have to pass the current account's ID. (I'm not sure if your current_user is the same as your current_account, you may wish to clarify this).
To compact your post creation into one line, you can do one of two things.
Turn the relationship between a user/author and a post into a two-way relationship. Check out the documentation http://guides.rubyonrails.org/association_basics.html where an order belongs_to a customer, and a customer has_many orders. You can customize the name of the relationship so that a post has an "author" instead of a user, by calling it "author" but then using the class_name parameter which I assume would take the value :user.
Add a after-create hook to the Post class, and set the author value to the same as the current user. I can't fill in much more detail about this without knowing anything about your user subsystem.
The params variable is just a hash, so something along these lines should work to give you a one liner:
#post = current_account.posts.build params[:post].merge({ :user_id => current_user.id })

Has many through associations with conditions

I am trying to add a condition to a has many through association without luck. This is the association in my video model:
has_many :voted_users, :through => :video_votes, :source => :user
I want to only get the voted_users whose video_votes have a value equal to 1 for that video. How would I do this?
I would suggest creating a model method within the video model class
Something like:
def users_with_one_vote
self.voted_users, :conditions => ['value = ?', 1]
end
Then in the controller use video.users_with_one_vote
Then testing is easier too.
Any chance you can change that column name from 'value'. Might give some issues (reserved?).
I'd do this in 2 stages:
First, I'd define the has_many :through relationship between the models without any conditions.
Second, I'd add a 'scope' that defines a where condition.
Specifically, I'd do something like:
class User < ActiveRecord::Base
has_many :video_votes
has_many :votes, :through=>:video_votes
def self.voted_users
self.video_votes.voted
end
end
class VideoVote
def self.voted
where("value = ?", 1)
end
end
class Video
has_many :video_votes
has_many :users, :through=>:video_votes
end
Then you could get the users that have voted using:
VideoVote.voted.collect(&:user).uniq
which I believe would return an array of all users who had voted. This isn't the exact code you'd use -- they're just snippets -- but the idea is the same.
Would
has_many :voted_users, :through => :video_votes, :source => :user, :conditions => ['users.votes = ?', 1]
Do the trick?
I found that defining this method in my model works:
def upvoted_users
self.voted_users.where("value = 1")
end
and then calling #video.upvoted_users does the trick.
The best way to do this without messing with the relations is by crafting a more complex query. Relations is not the best thing to use for this particular problem. Please understand that relations is more a "way of data definition" then a way of "bussiness rules definition".
Bussiness logic or bussiness rules must be defined on a more specifically layer.
My suggestion for your problem is to create a method to search for users who voted on your video only once. something like:
class Video < ActiveRecord::Base
def voted_once()
User.joins(:video_votes).where("video_votes.value == 1 AND video_votes.video_id == ?", this.id)
end
Rails is magical for many things, but complex queries still have to be done in a "SQL" way of thinking. Don't let the illusional object oriented metaphor blind you
As long as we are throwing around ideas, how about using association extensions.
class VideoVote
scope :upvotes, where(:value => 1)
end
class Video
has_many :voted_users, :through => :video_votes, :source => :user do
def upvoted
scoped & VideoVote.upvotes
end
end
end
Then you feel good about making a call with absolutely no arguments AND you technically didn't add another method to your Video model (it's on the association, right?)
#video.voted_users.upvoted

Modify the behavior of has_many or use scope?

I have a class that looks something like this:
class User < ActiveRecord:Base
has_many :users_companies
has_many :companies, :through => :users_companies
end
For plain users, I'd like user.companies to refer to the standard association method, but when a user is an admin, I want User.all (i.e., admins have access to all companies). The simplest way I can think of to implement this (and what I've always done in the past) is use a scope on the Company class, such as:
scope :accessible_by, lambda { |user| ... }
The only problem is that this just doesn't feel right. Instead of writing a controller action that includes:
#companies = Company.accessible_by(current_user)
I'd feel more comfortable writing
#companies = current_user.companies
Is there a good way to override the User#companies method to accommodate this kind of behavior? Or, should I be happy with using a scope on Company?
I'm wrestling with a similar problem. The only acceptable solution I can devise is an association extension, which overrides the query for admin users and passes normal users' queries, unmolested.
# this works for me in rails 3.1
class User < ActiveRecord:Base
has_many :users_companies
has_many :companies, :through => :users_companies do
def visible
if proxy_association.owner.admin?
UsersCompany.scoped
else
self
end
end
end
end
User.where(:admin => true).first.companies.visible == UsersCompany.all
I'm fairly new to Rails, but this is an interesting question so I figured I'd toss in my two cents. It seems that you should be able to extend your association in User with a companies method that checks self.is_admin? (or similar) and returns what you need. See http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many#461-User-a-block-to-extend-your-associations
Nice question. I was wondering if something like the following is an option you would consider
class User < ActiveRecord:Base
has_many :users_companies
has_many :companies, :through => :users_companies
def viewable_companies
admin? ? Company.all : self.companies
end
end
I know the naming is horrible but, you know, naming things is serious stuff :)

How to pass value from controller to model?

class Task < ActiveRecord::Base
has_many :notes, :as => :notable, :dependent => :destroy
has_many :work_times, :dependent => :destroy
end
class WorkTime < ActiveRecord::Base
belongs_to :task
end
class NotesController < ApplicationController
end
end
####
Any help please??
Since the relationship is a has_many you will need to work with a particular time, not the aggregate:
work_time = #task.work_times.last
work_time.start_time = Time.now
work_time.save!
In this case the last WorkTime record is selected and manipulated. Maybe you want to use the first, or select it with a condition:
work_time = #task.work_times.where(:active => true).first
There's a lot of ways to select the correct record to manipulate, but your question is somewhat vague.
If you're looking to create a new entry instead of modifying one, you might want to do this:
#task.work_times.create(:start_time => Time.now)
This is just exercising the ActiveRecord model relationship.
You would simply get the object and just change its value like :
user = User.first
user.username = 'changed_name'
user.save # and save it if you want
But, this is actually code that belongs to a model and should be wrapped by a model method.
As alluded to by a few of the other answers, you need an object of type WorkTime to pass the value to.
From the code you've posted it doesn't look like you've got such an instance. You can either find one (WorkTime.find.. ) or create a new one (WorkTime.new..)
It looks like you have an instance of a note (#note), though I'm not sure where that came from.. you might be able to fetch appropriate WorkTime objects using:
#note.task.work_times
or the first of these with:
#note.task.work_times.first

Pull objects from an association into an array in Rails

A user has_many :donations, a project has_many :donations, and a donation belongs_to :user and belongs_to :project.
I'm looking for a sensible way to extract the projects associated with a user (through donations) into an array.
I'm currently doing:
def index
#user = User.find params[:user_id]
#projects = []
#user.donations.each do |donation|
#projects << donation.project
end
end
I feel like I'm missing something obvious, as this seems lame. Is there a better way to do this?
Edit
I accidentally simplified this too far. A user can also be associated with a project through other models, so #projects = #user.projects isn't going to do what I need it to.
class User < AR::Base
has_many :donations
has_many :projects, :through => :donations
…
end
#user.projects
should work.
For gathering many association collections see my previous answer. You will need to adapt it to use the through associations (just treat them as normal has_masnys), but the same applies.

Resources