has_many通过但具有独特的来源和多个关联

时间:2012-12-31 00:03:09

标签: ruby-on-rails ruby-on-rails-3 has-many-through

  • 作为用户,我可以将自己喜爱的图书添加到我的图书清单中。
  • 另一位用户将同一本书添加到他们的图书清单中。
  • 数据库中只应有一本书的实例,但有2个用户/书籍关联。

    class User
      has_many :bookReferences
      has_many :books, through: :bookReferences
    
    class Book
      validates_uniqueness_of :title
      has_many :bookReferences
      has_many :users, through: :bookReferences
    
    class BookReference
      belongs_to :user
      belongs_to :book
    

第一个问题是,如果图书已经在系统中,则不会为第二个用户创建bookReference,因为该图书不是唯一的。

所以,如果这本书已经在系统中,我想在bookReference表中创建关联记录。

第二个问题,当用户删除图书时,我只希望它删除对该图书的引用,除非他们是引用该图书的唯一用户。

基本上,这是我正在努力实现的用法的完整概述:

    user1.books.first
      => id: 1, title: "Moby Dick"
    user2.books.first
      => id: 1, title: "Moby Dick"
    books.all
      => id: 1, title: "Moby Dick"


    user1.books.first.destroy
    user1.books.first
      => nil
    books.all
      => id: 1, title: "Moby Dick"
    user2.books.first
      => id: 1, title: "Moby Dick"
    user2.books.first.destroy
      => nil
    books.all
      => nil

更新

基于这些答案,也许我不太清楚。让我再尝试一次... 书籍控制器,有一个基本的CRUD创建方法:

    def create
      current_user.books.create(name: params[:book][:title])

通过当前设置has_many through association的方式,只有在本书唯一时才会创建该书。如果它已经在系统中,它将返回false。我想要它做的是创建与现有书籍的关联,就好像它是一个新的记录。这就是我目前在申请中所做的事情,但是感觉不对:

   def create
     book = Book.where(name: params[:book][:title]).first_or_create!
     BookReference.where(book_id: book.id, user_id: current_user.id).first_or_create!

然后第二个问题是用户从他们的帐户中删除了一本书。执行传统的CRUD destroy会将其从所有帐户中删除:

    def destroy
      book = current_user.books.find(params[:id])
      book.destroy

所以为了解决这个问题,我现在正在做以下事情。尽管如此,这感觉并不“正确”:

    def destroy
      book = current_user.books.find(params[:id])
      # if book was unique to user
      if BookReference.where(book_id: book.id).count == 1
        # remove book from system
        book.destroy  
      else
        # remove book reference but not book
        current_user.books.delete(book)
      end
end  

2 个答案:

答案 0 :(得分:0)

我对rails也有点新意,但这就是我想出来的。

book = user1.books.first
if book.users.size > 1 
  user1.books.first.destroy
else
  book.destroy 
end
# in order to delete the book's dependency, you will need
class Book
has_many :bookReferences, :dependent => :destroy

希望这有帮助。

注意: 如果您的模型名为BookReference,则将模型引用为:book_reference 在您的情况下,您需要更改为

class User
  has_many :book_references
  has_many :books, through: :book_references

class Book
  validates_uniqueness_of :title
  has_many :book_references
  has_many :users, through: :book_references

class BookReference
  belongs_to :user
  belongs_to :book

更新:

由于您希望新书在数据库中不存在时创建,因此您可以利用的一种rails方法是Book.find_or_create()。我不会详细介绍,但我觉得以后你会想要一个自动完成功能来查阅书籍。 在Autocomplete上查看此railscast。 Ryan Bates在设置自动填充功能方面表现出色,如果找不到则会创建新条目,这正是您所寻找的。

此外,如果您需要一种更简单的方法来允许用户添加多本图书,那么这是另一个教您了解Token Fields

的轨道广播

答案 1 :(得分:0)

当您使用has_many通过关联时,

rails会自动为您创建bookreference。

有很多人通过在你的用户模型中创建一个名为:book_ids的“隐藏列”

因此,在您的表单中,您希望将图书“保存”给用户

<%= form_for (@user) do |f| %>
.
.
.
<%= f.label :book_ids %>
<%= f.select :book_ids, Book.all.collect {|x| [x.name, x.id]}, {}, :multiple => true %>

应自动填充您的bookreference,您可以在视图中显示它。编辑和销毁也会自动为您完成。当你删除或编辑它时,它只是书籍参考表。

如果您想了解更多关于协会的信息,请访问

http://api.rubyonrails.org/

并搜索ActiveRecord :: Associations