Rails novice here. Say I have two tables, Owners (owner_id, name) and Cats (cat_id, cname). They're associated via a one-to-many relationship where an owner can own many cats but a cat can only have one owner. Given an input of owner_id, I want to retrieve the cat name and id for each cat owned by that Owner, and store it as an array of hashes (i.e. [{:cat_id => 1, cname => "cat1"}, {:cat_id => 2, cname = "cat2"}].
So far I have something like output = [Owner.find(owner_id).join(:Cats)] but I'm not sure how to complete the statement. Thank you so much.
Owner.find(owner_id).cats will give you what you want.
Try this:
Cat.all(:conditions => ['owner_id = ?', owner_id])
Or:
Owner.find(owner_id).cats
That one assumes you have has_many :cats in your Owner model. If you need a list of hashes instead of active record objects you can use .collect to transform your set.
Related
I have an API in which i have a HABTM relationship between Characters and Movies (a movie has many characters and a character has many movies). I'm trying to set up a filter with has_scope gem so i can do something like
/api/characters?by_movie=:movie_id so instead of getting all the Characters from index in my CharactersController, i only get the characters that take part in a specific movie.
The way the relationship is set up allows me to do something like Characters.find(1).movies -> returns the list of movies of that character. And if i send a POST to character and i want to add movies to it i can do it like "movie_ids":[1,2].
I have tried this approach with no success:
in my Character.rb scope :by_movie, -> id, {where(movie_ids: id)} and scope :by_movie, -> id, {where(movies: id)} and in my controller: has_scope :by_movie, using: :id
The documentation in has_scope is very little, i wasn't able to find my specific problem, i hope some of you can help me, thank you.
Well, i figured it ou (sort of)
app/models/movie.rb
has_and_belongs_to_many :genres
scope :by_genre, -> (genres) {
genre_array = genres.split(',') # this turns the genre string into an array
joins(:genres).where(genres: genre_array) # this joins the genres table to our movies model, these tables are related already by a HABTM relationship.
}
And in our app/controllers/movies_controller.rb
has_scope :by_genre
#movies = apply_scopes(Movie).all
The only problem i can't solve is that if i pass let's say
localhost:3000/studios/movies?by_genre=1,2,3 #This is what your GET should look like. 1, 2 and 3 are the id's of the Genres the movie has.
say a movie has all three genres, so when i send this, i get as a result that one movie three times, and not only one result. So instead of comparing our genres_array with genre_ids array, it's sort of comparing each element of genres_array with genre_ids, one at a time. I haven't been able to solve this problem yet.
SO i have active relation like:
[[id:1, post_id: 10, user_id:2][id:2, post_id:11, user_id:2]]
When i say relation.uniq, it doesn't return one value, because id's of objects are different(which is obvious behavior).
How do i get only unique object, but not by full fields, but only by some specific field, for example (:user_id)
I've tried to use distinct, but that returns me only array of fields, instead of active relation or objects
As result, i'd want to get an active relation where, field user_id would be unique
Though i'm not sure if this is good idea, maybe i should use some other methodics to get what i want, because i'm starting to understand, that this is not the best idea
How did you use distinct? When I run
> Model.select('DISTINCT column')
it seems to return an object of class ActiveRecord::Relation.
Use pluck.
Model.distinct.pluck(:user_id)
Examples:
Person.pluck(:id)
# SELECT people.id FROM people
# => [1, 2, 3]
Client.distinct.pluck(:role)
# SELECT DISTINCT role FROM clients
# => ['admin', 'member', 'guest']
I have a User model with a name attribute. In my PagesController, I want to set an instance variable equal to all of the User objects, but I want to order them based on last name. I know I can do:
#sortedusers = User.order(:name => :asc)
to order the users based on their name attribute, but how do I do it based on last name? (i.e. how do I order the Users based on the last word of their name attributes?
Thanks.
Define a virtual attribute last name in your model
def last_name
name.split(' ').last
end
and then
User.order(:last_name => :asc)
I'd suggest storing the users' last name separately. Barring that, you can use sort_by, like so:
items.sort_by! { |a| a.split(' ') } or something along those lines. Note, this most likely cannot be used straight, it's merely intended to point you in the right direction, as most of my answers are.
I'm doing an app for a membership database.
Each person may have a partner. When it comes to displaying the list, I only want to have one row for each family, so at the moment I'm comparing first names and not displaying the row if the person's name is second. Like this
person.first_name != [person.first_name, person.partner.first_name].sort[0]
This means each family only gets displayed once, not twice - once for each partner.
And I'm doing this in the view.
There must be a better way of doing this, and it'd be really great if I could do it at the database level. I'm using postgresql if that makes a difference.
Edit
Sorry if it was unclear.
Say Person 1 has the first_name "Edward" and Person 2 has the first_name "Fay". Edward and Fay are married.
I only want to show them once in my list - I want a row to look like this
Surname First name Address etc
Mysurname Edward ....
Fay
I don't want to display it again with Fay first because I've got both Fay and Edward in list of people, so I use the ruby in the first part of the question to check if I should display the row - it compares their first names and only does the row if the person has a fist name that's before his/her partner's first name.
Here's the relevant part of my person model
class Person < ActiveRecord::Base
has_one :relationship_link, :foreign_key => :person_id, :dependent => :destroy, :include => :partner
has_one :partner, :through => :relationship_link, :source => :person_b, :class_name => "Person"
I hope that's clearer
You need to use DISTINCT ON or GROUP BY. In postgres you need to be careful to group by everything that you are selecting. If you only need to get the last names you can select("DISTINCT ON(last_name) last_name").pluck("last_name"). You will only get an array of last names though.
Maybe you can get records if you order by every other fields in your table, like this:
select("DISTINCT ON(people.last_name) people.*").order("people.last_name ASC, people.first_name ASC, people.field2 DESC, people.field3 ASC...")
You need to order by every attribute so the result is not ambigious.
For this case, i would create a data structure (a Hash) to store people instances given a specific surname. Something like this:
def build_surnames_hash(people_array)
surnames_hash = {}
people_array.each do |person|
last_name = person.last_name
surnames_hash[last_name] ||= []
surnames_hash[last_name] << person
end
surnames_hash
end
That way, you can iterate over the hash and display people using their surnames stored as hash's keys:
surnames_hash = build_surnames_hash(Person.all)
surnames_hash.each do |surname, person_instances_array|
# display the surname once
# iterate over person_instances_array displaying their properties
end
I have the following objects: Products, Ratings, and Users. I am trying to filter and select a number of Products that the User owns (through a has_many :through relationship with UserProducts) and average a certain column the Ratings table that matches their User ID and the correct Product ID.
So, my function is something along these lines:
def find_rating(criteria)
product = self.products.find(:all, :conditions => ["criteria = ?", criteria])
rating = self.ratings.where("product_id = ?", product).average(:overall)
end
I think that I'm going about this the wrong way, because I'm trying to find a product_id by passing an entire array of data consisting of multiple products. But, I think of using a more traditional loop and that seems convoluted. Can someone point me in the right direction for solving this problem? Thanks!
If product is a single entry, as it appears to be in your code, I would do this:
rating = self.products.find_by_criteria(criteria).ratings.average(:overall)
If it's an array of products, this method may help you: http://apidock.com/rails/ActiveRecord/Batches/ClassMethods/find_each