【问题标题】:Is there a way to scope acts_as_list based on column in another table?有没有办法根据另一个表中的列来确定 acts_as_list 的范围?
【发布时间】:2023-02-10 11:10:20
【问题描述】:

TLDR: 有没有办法将acts_as_list 的范围限制在另一个表中

class SprintTodo < ApplicationRecord
  belongs_to :sprint
  belongs_to :todo
  acts_as_list scope: [:sprint, :todo.status]
end

我有两张桌子和一张连接桌子。

  1. Todo(name, position, status, parent, children, ...)
  2. SprintTodo(todo_id, sprint_id, position)
  3. Sprint(name, start_date, end_date, ...)

    Todo 根据其父节点(树)拥有自己的位置,而SprintTodo 根据其状态在看板中持有位置。

    我现在面临的问题是我无法进入Todo 表来确定它的范围。一种解决方案(虽然不好)是在SprintTodo 中也复制Todo 状态,但那将是糟糕的设计。

    有没有其他方法可以根据状态确定范围?

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-5 acts-as-list


    【解决方案1】:

    将状态列添加到 SprintTodo 可能会更简单。但是还有离开:

    class SprintTodo < ApplicationRecord
      belongs_to :todo
      belongs_to :sprint
    
      acts_as_list scope: "sprint_todos.id IN (#{todo_status_sql}) AND sprint_todos.sprint_id = #{sprint_id}"
    
      def todo_status_sql
        SprintTodo.select(:id).joins(:todo).where(todo: { status: todo.status }).to_sql
      end
    end
    
    Sprint.create!
    Todo.create!([{ status: :one }, { status: :one }, { status: :two }, { status: :two }])
    Sprint.first.todos << Todo.all
    Sprint.create!
    Sprint.second.todos << Todo.create(status: :one)
    
    
    >> SprintTodo.all.as_json(only: [:position, :sprint_id], include: {todo: {only: [:status, :id]}})
    => 
    [{"position"=>1, "sprint_id"=>1, "todo"=>{"id"=>1, "status"=>"one"}},
     {"position"=>2, "sprint_id"=>1, "todo"=>{"id"=>2, "status"=>"one"}},
    
     {"position"=>1, "sprint_id"=>1, "todo"=>{"id"=>3, "status"=>"two"}},
     {"position"=>2, "sprint_id"=>1, "todo"=>{"id"=>4, "status"=>"two"}},
    
     {"position"=>1, "sprint_id"=>2, "todo"=>{"id"=>5, "status"=>"one"}}]
    #             ^               ^                               ^
    #     positioned        by sprint                 and todo.status  
    

    https://www.rubydoc.info/gems/acts_as_list/0.8.2/ActiveRecord/Acts/List/ClassMethods#acts_as_list-instance_method


    更新

    我什么都没看到acts_as_list支持对状态更改重新排序的代码。更改发生在Todo,但所有回调都需要更新位置SprintTodo

    https://github.com/brendon/acts_as_list/blob/v1.1.0/lib/acts_as_list/active_record/acts/callback_definer.rb#L6-L16

    第一种方法是创建一个新的SprintTodo

    class Todo < ApplicationRecord
      has_many :sprint_todos
    
      after_update do
        new_sprint_todo = sprint_todos.first.dup
        sprint_todos.first.destroy
        new_sprint_todo.position = nil
        new_sprint_todo.save!
      end
    end
    

    另一种方法是手动触发其中一些回调:

    class SprintTodo < ApplicationRecord
      attr_accessor :scope_changed
      #...
    end
    
    class Todo < ApplicationRecord
      has_many :sprint_todos
    
      before_update do
        sprint_todo = sprint_todos.first
        sprint_todo.scope_changed = true
        sprint_todo.send :check_scope
        sprint_todo.save!
      end
    end
    

    必须设置@scope_changed,否则check_scope不会做任何事情:
    https://github.com/brendon/acts_as_list/blob/v1.1.0/lib/acts_as_list/active_record/acts/list.rb#L430-L441

    【讨论】:

    • 我同意。我没有将 status 添加到 SprintTodos 的原因是 statusTodo 的一部分,而不是 SprintTodo。对于尚未分配给 SprintTodo,这会造成一种奇怪的情况。
    • 还有一件事,如果我更改Todo 的状态,它不会自动更新它在SprintTodo 中的位置。有没有办法强制重置其位置?
    • @Sawaid 查看更新。我不确定它的效果如何,您可能需要稍微调整一下才能使用您的代码。
    猜你喜欢
    • 2021-10-10
    • 2019-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多