Ruby sequel 如何实现 many-to-many 的附加属性

ruohanc · 2012年06月18日 · 最后由 ruohanc 回复于 2012年06月22日 · 4419 次阅读

简而言之我正在写的是一个 XMPP 机器人,作用是帮助我去查看小说是否有更新 (从百度贴吧上找).

所以数据库我是这样想的,一个 users 存放用户信息,一个 fictions 存放小说的信息,一个 checked_lists 存放已经检索到的小说章节记录,一个 subscriptions 存放 user 和 fiction 的订阅关系。

但是如何保存这个用户的某本小说的进度呢?我是考虑在 subscriptions 表里面添加一列 foreign_key 来保存最后阅读的 checked_list 记录,那么就需要读取 subscriptions 表的信息 (这可怎么做啊)

下面是我的 model

class User < Sequel::Model
  # create_table? :users do
  #   primary_key :id
  #   index :id
  #   String :account
  #   Fixnum :total_count, :default => 0
  # end
  many_to_many :fictions, :join_table => :subscriptions
end

class Fiction < Sequel::Model
  # create_table? :fictions do
  #   primary_key :id
  #   index :id
  #   String :name
  #   String :url
  # end
  many_to_many :users, :join_table => :subscriptions
  one_to_many :check_lists
end

class CheckList < Sequel::Model
  # create_table? :check_lists do
  #   primary_key :id
  #   foreign_key :fiction_id, :fictions
  #   Time :created_at
  # end
  many_to_one :fiction
end

还有我中间表的 schema

create_table? :subscriptions do
    primary_key :id
    foreign_key :fiction_id, :fictions
    foreign_key :user_id, :users
    foreign_key :check_id, :check_lists
  end

问题:howto 获得 subscriptions 表中的 :check_id?

Advanced Associations 里面找到一些关于实现 ActiveRecord 中的 has_many :through 关系的资料,他这样说的:

ActiveRecord

class Author < ActiveRecord::Base
  has_many :authorships
  has_many :books, :through => :authorships
end
class Authorship < ActiveRecord::Base
  belongs_to :author
  belongs_to :book
end

@author = Author.find :first
@author.books

Sequel

class Author < Sequel::Model
  one_to_many :authorships
  many_to_many :books, :join_table=>:authorships
end
class Authorship < Sequel::Model
  many_to_one :author
  many_to_one :book
end

@author = Author.first
@author.books

但是在 ActiveRecord 里面可以用 Author.first.authorships 来找到关系对象然后取出附加属性,在 Sequel 里面却不行..

另外据说我这样的数据库设计有点奇怪。能不能给我提点意见?

啊~~最后无意发现,好像这样能行:

加上 Subscription Model

class Subscription < Sequel::Model
  # create_table? :subscriptions do
  #   primary_key :id
  #   foreign_key :fiction_id, :fictions
  #   foreign_key :user_id, :users
  #   foreign_key :check_id, :check_lists
  # end
  many_to_one :user
  many_to_one :fiction
end

然后用 filter 语句

Subscription.filter(:user => User.first)

就行了

为了让它看起来有 ActiveRecord 那样舒服直观,于是给 User Model 加上了这个属性

class User
  def subscriptions
    Subscriptions.filter(:user => self)
  end
end

啊..没人鸟..好伤心....

主要是用 Sequel 的确实是太少,而我之前用 Sequel 的项目还是单表操作,so。。。

另外,建表的那些语句可以改成如下

class User < Sequel::Model
  plugin :schema

  unless table_exists?
    set_schema do
      primary_key :id
      index :id
      String :account
      Fixnum :total_count, :default => 0
    end
    create_table
  end
  many_to_many :fictions, :join_table => :subscriptions
end

可以在数据库没有 User 表的时候自动自动建表

#5 楼 @ywjno 我赞!! 居然还能这样.... Sequel 的文档也是看的有点头晕...

那请问不写 Rails 的时候需要用数据库的话,一般怎么处理这个问题呢..?

匿名 #7 2012年06月19日

在导数据的时候用过,不一定非要建 model

Sequel.connect("sqlite://#{Rails.root.to_s}/db/zones.db") do |db|
  db[:T_Province].each do |p|
    puts "create province #{p[:ProName]}"
    Province.create :name => p[:ProName], :code => p[:ProSort], :mark => p[:ProRemark]
  end
 end

#6 楼 @ruohanc Sequel 的文档是有些头晕,有些东西就写在很不起眼的那么个页面上

其实不用 Rails 的话 ActiveRecord 也是能单独使用的,而其他的除了 Sequel 外我还知道的另一个是 DataMapper,不过这个就没用过了

#8 楼 @ywjno 单独使用 ActiveRecord 有点麻烦的样子。也同时抱着试试看别的 gem 的态度上了 sequel.. 因为看到有说 Sinatra 推荐用这个..

#7 楼 @sharp 额..没看懂你想说啥

#9 楼 @ruohanc 7 楼想说的意思是 Sequel 可以直接取得数据库的连接然后对表进行操作,那个 Province 应该是继承了 ActiveRecord 的 class

目前只用 Sequel 做遗留数据迁移,AR 做业务逻辑爽很多

#11 楼 @rainchen 好像不用 Rails 的时候用 AR 很不方便诶..比如 migration 怎么做捏.....我搜了好多'active record without rails' 的资料..没什么特别帮的上忙的啊.. #10 楼 @ywjno

Sequel 好像不能实现中间表的限制诶...

订阅关系一一对应,就是说出现两个 同一个人订阅同一个小说 的记录是 不科学 的~

代码表达下意思就是

user = User.first
fic = Fiction.first
user.add_fiction(fic) # 成功
user.add_fiction(fic) # 也成功了, 但这不是我希望的

我希望在 model 层上就控制这个行为。但是找不到对应的方法。

我已经尝试在中间表 Subsciption 中加入 validates_unique [:user_id, :fiction_id] 的验证。这样的话,直接 Subsciption.create 会成功阻止,但是 user.add_fiction 依然会增加重复项

需要 登录 后方可回复, 如果你还没有账号请 注册新账号