Rails where with join, but return object of parent?
Suppose I have a class that represents a list of movies, belonging to a user:
class User < ActiveRecord::Base
has_one :movie_list
end
class MovieList < ActiveRecord::Base
belongs_to :user
has_many :movie_list_movie_relationships
has_many :movies, :through => :movie_list_movie_relationships
end
class Movie < ActiveRecord::Base
has_many :movie_list_movie_relationships
has_many :movie_lists, :through => :movie_list_movie_relationships
end
class MovieListMovieRelationship < ActiveRecord::Base
开发者_高级运维 belongs_to :movie
belongs_to :movie_list
end
Is there any way to have a method on MovieList that returns a MovieList containing only movies with, for example, a specific release year. I want something like:
class MovieList < ActiveRecord::Base
....
def for_year year
movies.where(:year => year)
end
end
EXCEPT that I want the result to be a MovieList itself, whereas this returns an array of Movies. So I could do, for example:
list = User.first.movie_list.for_year(1999)
list.id # would give the id of the MovieList = User.first.movie_list.id
list.movies.first.id # would give the id of the first movie in list
I have a feeling that I'm either a) faced with this problem because my design is bad, and it would be great if someone could explain how I should design this, or b) asking for something relatively mundane but I just don't know the right terminology.
Thanks!
Edit
I have specific reasons for wanting a MovieList
and not an array of Movie
s. For one, I've written the question with User has_one :movie_list
but in reality a User has_many :movie_lists
(e.g., "Movies I have on DVD", "Movies I want to see", etc). That's why I don't just use a simple join between User
and Movie
. I want a MovieList
because I want the id
of the list to get passed around to make it easy to add a movie to a specific list; I have a MovieListMovieRelationshipController
. I think what I'm going to end up having to do is fetch the movies as an array and just pass around the list id manually.
Edit 2
Here's what I ended up doing, as per @normalocity and the ensuing comments. Any further comments/suggestions are appreciated :)
class MovieList
...
def limit_to_year year
new_list = MovieList.new(self.attributes)
new_list.id = self.id
new_list.movies = []
limited_movies = movies.where(:year => year)
new_list.movies = limited_movies
return new_list
end
end
You can't do what you're hoping for directly, no, but it's simple enough to create an empty MovieList
, and then add the array of movies from your for_year
method to the new MovieList
. You can even make the query for the array of associated Movie
objects faster/simpler via a named_scope
.
- Create a named scope to get all Movies from a specific year:
In Movie
class:
named_scope :for_year, lambda { |year| {:conditions => ["year = ?", year]} }
Now, every time you call Movie.for_year("1945")
you get back an array of Movie objects from that year.
Then...
- Create a new
MovieList
(that belongs to NO user), and add the results from #1 to the list
This might work:
movies1945 = MovieList.create(:movies => Movie.for_year("1945")
If it doesn't work, you might have to split up the creation of the new MovieList
object, and the assignment of values to the Movie
collection.
If you need to distinguish between a simple list of Movies (what's the difference between that and an Array, I don't know, but if that distinction is necessary), and the Movies
that a particular user has in their collection, then you could separate your classes into MovieList
(just a list of movies) and a Collection
(the movies that a given user has).
If you don't need that, then the 1945 movie list could either have a nil
user, or (say your web site is called imdb.com) you could create a dummy user account called "imdb"
, and have the list belong to that user.
精彩评论