Rails 5.1、删除多条带条件的记录

Rails 5.1, delete multiple records with conditions

我尝试根据一些条件删除数据库中的几条记录。在我谈论我的问题之前,我将解释我的应用程序是如何工作的。

用户可以创建组和 link。该用户拥有他为 link 创建的相同组。然后其他用户可以通过成员模型提供令牌(在创建组时自动创建)来加入该组。其中成员在数据库中有 user_id 和 group_id。然后,一旦用户成为组的一部分,他就可以通过组link 模型在组中共享他创建的link。其中 grouplink 有 groupd_id 和 link_id。

我设法编写了一个事实,即如果一个组中没有成员,该组就会被破坏。但是,如果他离开组,我如何设法删除组中共享的 links 用户? (当用户自动销毁时,所有 link 都被删除,所以我认为共享也应该消失,我必须尝试一下')。当用户离开组时,我会销毁他的成员记录,他就走了,但 link 仍然存在。我显示了共享的 links,你可以踢用户(成员销毁)的事实以及组中显示的组成员列表 btw。

我考虑了一些事情。我在控制台写下我做了什么

g = Group.find(66)
u = g.users.find(1)
u.links

然后给我所有来自组中用户的 link。在那旁边

g.grouplinks

会给我群组中所有共享的 link。

g.grouplinks.map(&:link_id) returns [16, 17, 14, 13, 15]
u.links.map(&:id) returns [13, 15]

现在我可以在这里做什么?我的用户要离开群组。成员记录被销毁,我如何根据用户拥有的 links 销毁那些组 links?
有没有我还不知道的魔术?

感谢您的帮助。

编辑

class User < ApplicationRecord

    has_secure_password


  has_many :links, dependent: :destroy
  has_many :grouplinks, dependent: :destroy

  has_many :members, :dependent => :destroy
  has_many :groups, :through => :members
  has_one :owned_group, foreign_key: "owner_id", class_name: "Group"
end

class Member < ApplicationRecord

  belongs_to :user
  belongs_to :group

  validates :user_id, :presence => true
  validates :group_id, :presence => true
  validates :user_id, :uniqueness => {:scope => [:user_id, :group_id]}

end

class Link < ApplicationRecord

    has_many :grouplinks, :dependent => :destroy
    belongs_to :user

end

class Grouplink < ApplicationRecord

    belongs_to :group
    belongs_to :link

end

class Group < ApplicationRecord
has_secure_token :auth_token

    has_many :members, :dependent => :destroy
    has_many :users, through: :members, source: :user
    belongs_to :owner, class_name: "User"

    has_many :links, through: :grouplinks
    has_many :grouplinks, :dependent => :destroy

    def to_param
        auth_token
    end

end

我认为实际上我可以在link组中添加user_id,这样我就可以delete_all根据link中的user_id s 和组 links。不知道该怎么做,也不知道是否有更好的解决方案。

编辑 2

我在模型中尝试了您的解决方案。实际上它很聪明,我没有想到这一点...... 现在的问题是创建我的组link(分享link)。我有这个:

  def create
    user = current_user if current_user
    group = user.groups.find_by(auth_token: params[:auth_token])
    share = group.id
    group_link = group.grouplinks.build(link_id: params[:link_id])
    gl = group.grouplinks
    if gl.where(group_id: share).where(link_id: params[:link_id]).exists?
        flash[:error] = "You shared this link in '#{group.name}' already."
        redirect_to mylinks_path
    else
        if group_link.save
          group_link.toggle!(:shared)
          flash[:success] = "You shared your link in '#{group.name}'."
          redirect_to mylinks_path
        else
          render 'new'
        end
    end
  end

这显然不再有效,当我尝试共享 link 时出现此错误:First argument in form cannot contain nil or be empty <%= form_for @grouplink do |f| %>.

我试过这样改:

def create
    group = Group.find_by(auth_token: params[:auth_token])
    share = group.id
    group_link = group.grouplinks.build(link_id: params[:link_id])
    gl = group.grouplinks
    if gl.where(group_id: share).where(link_id: params[:link_id]).exists?
        flash[:error] = "You shared this link in '#{group.name}' already."
        redirect_to mylinks_path
    else
        if group_link.save
          group_link.toggle!(:shared)
          flash[:success] = "You shared your link in '#{group.name}'."
          redirect_to mylinks_path
        else
          render 'new'
        end
    end
  end

但它也不起作用

您要找的是dependent :delete_all

在你的 Group 模型中,你应该有这样一行:

has_many :links, dependent :delete_all

这就是说,该群有很多链接,如果你销毁该群,则销毁所有相关链接。

在您的 Member 模型中,您可以使用 after_destroy 回调在成员记录被销毁后销毁群组中的所有用户链接:

class Member < ApplicationRecord
  after_destroy do |record|
    record.user.links.each { |link| link.grouplinks.where(group_id: record.group.id).destroy_all }
  end

  belongs_to :user
  belongs_to :group

  ...

end

此外,我建议您将 Member 更改为 Membership 以便更清楚。

怎么样:

class Member < ApplicationRecord

  belongs_to :user
  belongs_to :group
  has_many :group_links, dependent: :destroy

  validates :user_id, :presence => true
  validates :group_id, :presence => true
  validates :user_id, :uniqueness => {:scope => [:user_id, :group_id]}

end

class Grouplink < ApplicationRecord

  belongs_to :link
  belongs_to :member

end

现在,当 Member 记录被销毁时(即 user 被踢出或离开 group),任何 linksgroup(即group_links)也被销毁。但是,如果 user 在另一个 group 中共享了 link,则 link 将继续与其他 groups 共享。

正如@Pablo 在评论中提到的,您可能还想这样做:

class Group < ApplicationRecord
  has_secure_token :auth_token

  has_many :members, :dependent => :destroy
  has_many :grouplinks, through: :members
  has_many :users, through: :members, source: :user
  belongs_to :owner, class_name: "User"

  has_many :links, through: :grouplinks

  def to_param
      auth_token
  end

end

这将允许你做:

group.grouplinks

我也同意@amr-el-bakry 的观点,Member 有点令人困惑。我建议 GroupUser,因为它很清楚地表明它是 GroupUser 之间的关联。

此外,我认为说 GroupLinkGrouplink 可能更传统一些。或者,如果您想坚持基于关联 类 的命名,也许 MemberLink。如果将 Member 更改为 GroupUser,则可能 GroupUserLink

我认为您的 create 代码应该类似于:

  def create
    if group
      if member
        if link
          unless group_link
            @group_link = member.group_links.build(link: link)
            if group_link.save
              group_link.toggle!(:shared)
              flash[:success] = "You shared your link in '#{group.name}'."
              redirect_to mylinks_path
            else
              render :new
            end
          else
            flash[:error] = "You shared this link in '#{group.name}' already."
        else
          flash[:error] = "That link does not exist."
          redirect_to somewhere #fix this
        end
      else
        flash[:error] = "You must be a member of this group to add a link."
        redirect_to somewhere #fix this
      end
    else
      flash[:error] = "There is no group with that token."
      redirect_to somewhere #fix this
    end
  end

private

  def group
    @group ||= Group.find_by(auth_token: params[:auth_token])
  end

  def member 
    @member ||= current_user.members.where(group: group)
  end

  def link
    @link ||= Link.find_by(id: params[:link_id])
  end

  def group_link
    @group_link ||= member.group_links.where(link: link)
  end

可以将其写成:

  def create

    flash[:error] = "There is no group with that token."
    redirect_to somewhere unless group

    flash[:error] = "You must be a member of this group to add a link."
    redirect_to somewhere unless member

    flash[:error] = "That link does not exist."
    redirect_to somewhere unless link

    flash[:error] = "You shared this link in '#{group.name}' already."
    redirect_to mylinks_path if group_link

    flash[:error] = nil

    @group_link = member.group_links.build(link: link)
    if group_link.save
      group_link.toggle!(:shared)
      flash[:success] = "You shared your link in '#{group.name}'."
      redirect_to mylinks_path
    else
      render :new
    end

  end

但我不记得那些重定向是否会让你心痛。

我认为最好的想法是 group_links 属于一个成员和一个 link(而不是一个组和一个 link)。而一个会员(不是用户)有很多group_links。当您销毁成员时,它会销毁 group_links。

编辑

这是 jvillian 在他的回答中建议的,就在我之前。所以我相信他的回答是正确的(我在评论中提出了一些小的改进,jvillian 肯定会接受并添加 :-)。

EDIT2

关于您在应用jvillian建议后遇到的问题,在创建新组时link,必须由成员(而不是组)完成。因此,在创建操作中,您必须搜索成员(按 user_id 和 group_id)并将组 link 创建为 member.grouplinks.build