【问题标题】:More than one has_many :through?不止一个 has_many :through?
【发布时间】:2015-03-14 19:54:28
【问题描述】:

所以。我有用户和电影。用户观看了一些电影而不是其他电影。我想这样表达这种关系:

注意:

  • 不确定是否重要;但电影没有必须与用户联系;它们可以独立存在(即电影 1 与用户 2 没有关系)。用户也可以独立存在;他们不必看过或未看过电影(此处未显示,但您明白了)
  • 一个用户可以观看一部电影,但另一个用户不观看(灰色与黑色连接)

我最初的反应是这是has_many :through 关系,类似于:

/models/user.rb:

def User
    has_many :movies, :through => :unwatched_movies
    has_many :movies, :through => :watched_movies
end

/models/movie.rb:

def Movie
    has_many :users, :through => :unwatched_movies
    has_many :users, :through => :watched_movies
end

但首先,那段代码肯定行不通……

我希望能够查询u.unwatched_movies(其中uUser 的一个实例,这似乎与上述不符。

我感觉这与:source:as 有关...但我感觉有点失落。我是否认为这是一个 3 级层次结构,我需要 UserUnwatchedMovieList/WatchedMovieListMovie 的模型? This question 感觉非常接近,但我似乎无法让它在这种情况下工作。

任何关于如何编写这些模型和迁移的帮助都会非常有帮助。谢谢!

【问题讨论】:

    标签: ruby-on-rails ruby activerecord has-many-through rails-models


    【解决方案1】:

    您正试图建立一种省略关系——“无与伦比的电影”。这不是一个好主意,您应该建立电影观看的历史记录(即watched_movies),但是对于未观看的电影,您会希望找到所有电影减去已观看的电影。然后将其粘贴到 User 中的一个函数中,如下所示:

    def unwatched_movies
      Movie.where("id NOT IN ?", self.watched_movies.collect(&:movie_id))
    end
    

    【讨论】:

    • 感谢您的快速回复!我完全理解你关于遗漏关系的观点,但这并不是我想要的。我希望用户能够拥有并策划一个“未观看的电影”列表。我并不总是将“unwatched_movies”的意思简单地表示为“我没有看过的整个电影目录中的所有电影”,我的意思是“我特别标记但我没有看过的电影列表”。 (这是否清楚?)然后,最终,用户可以将他们的“未观看电影”之一标记为“已观看”(这将改变其关联)。
    • 啊,我明白了。所以它实际上是一个愿望清单。我刚刚注意到你试图调用'User.watched_movies' 这不起作用,你首先需要一个用户实例,如下所示:'@user = User.first', '@user.unwatched_movies' 搜索类方法和实例方法的区别。
    • 是的,对不起,我会在问题中编辑它。我在做u = User.first 然后u.unwatched_movies (我擅长类/实例方法)
    • 你可以用watched_movie_ids代替collect。
    【解决方案2】:

    这是我的解决方案

    创建这些模型

    class User < ActiveRecord::Base
        has_many :user_movies
        # Use a block to add extensions 
        has_many :movies, through: :user_movies, source: 'movie' do
            # this is an extension
            def watched
                where('user_movies.watched = ?', true)
            end
            def unwatched
                where('user_movies.watched = ?', false)
            end
        end
    end
    
    class Movie < ActiveRecord::Base
        has_many :user_movies
        has_many :watchers, through: :user_movies, source: :user do
            # users who is an effective watcher 
            def watchers
                where('user_movies.watched = ?', true)
            end
            # users how marked it but did not watch it yet
            def markers
                where('user_movies.watched = ?', false)
            end
        end
    end
    
    class UserMovie < ActiveRecord::Base
      belongs_to :user
      belongs_to :movie
    end
    
    class CreateUserMovies < ActiveRecord::Migration
      def change
        create_table :user_movies do |t|
          t.belongs_to :user, index: true
          t.belongs_to :movie, index: true
          t.boolean :watched, default: false, null: false
    
          t.timestamps null: false
        end
        add_foreign_key :user_movies, :users
        add_foreign_key :user_movies, :movies
      end
    end
    

    然后用于查询

    @user = User.first
    @user.movies.watched
    @user.movies.unwatched
    
    @movie = Movie.first
    @movie.watchers.watchers
    @movie.watchers.markers
    

    【讨论】:

      【解决方案3】:

      以下关联集应涵盖您能够明确标记已观看和未观看的电影的用例。它使用了一个名为user_movies 的连接表,它只包含以下字段:user_id, movie_id, and watched

      class User
        has_many :unwatched_user_movies, -> { where(watched: false) }, class_name: 'UserMovie'
        has_many :unwatched_movies, through: :unwatched_user_movies, class_name: 'Movie'
      
        has_many :watched_user_movies, -> { where(watched: true) }, class_name: 'UserMovie'
        has_many :watched_movies, through: :watched_user_movies, class_name: 'Movie'
      end
      
      class UserMovie
        belongs_to :movie
        belongs_to :user
      end
      
      class Movie
        has_many :user_movies
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-05-03
        • 1970-01-01
        • 2019-03-04
        • 2016-01-20
        • 1970-01-01
        • 2015-12-17
        • 2014-09-01
        • 2011-07-24
        相关资源
        最近更新 更多