【问题标题】:Ruby on Rails ActiveRecord data flowRuby on Rails ActiveRecord 数据流
【发布时间】:2018-11-11 20:39:39
【问题描述】:

我目前正在尝试构建一个与Trello 类似的应用程序。我有users 可以创建boards。每个用户可以拥有不同的role,具体取决于他们所在的板。因为我对 Rails 相当陌生,所以我只想确保我遵循“Rails 方式”和最佳实践。这是我的架构:

create_table "boards", force: :cascade do |t|
    t.string "title"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "user_boards", force: :cascade do |t|
    t.integer "user_id"
    t.integer "board_id"
    t.integer "role"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "users", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer "sign_in_count", default: 0, null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.inet "current_sign_in_ip"
    t.inet "last_sign_in_ip"
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
  end

  add_foreign_key "lists", "boards"

如您所见,我将role 属性添加到UserBoard 连接表中。一个User has_many BoardsBoard has_many Users 两个through: UserBoard。根据UserBoard,它们可以有不同的role

user_board.rb

class UserBoard < ApplicationRecord
  belongs_to :user
  belongs_to :board
  enum role: { admin: 0, member: 1 }
end

用户.rb

class User < ApplicationRecord
  has_many :user_boards
  has_many :boards, through: :user_boards
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
  validates :name, presence: true, length: { maximum: 50 }
  validates :email, presence: true
end

board.rb

class Board < ApplicationRecord
  has_many :user_boards
  has_many :users, through: :user_boards
  has_many :lists, dependent: :destroy
  include AssociateUsersToBoards

  def self.assign_board_to_user(user, board)
    AssociateUsersToBoards.build_association(user, board)
  end
end

我只是想弄清楚这是否是处理此问题的最佳方法以及是否有更好的设置方法,以便我的查询和更新可以更简洁一些。现在,当User 创建Board 时,您可以看到我正在使用ActiveRecord:create 函数。我使用create 的原因是因为当我尝试使用newbuild 时,连接表中的关联没有建立。我无法执行current_user.boards.new(board_params)current_user.boards.build(board_params),这对我来说似乎是错误的:

def create
  @board = current_user.boards.new(board_params)

  respond_to do |format|
    if current_user.save
      format.html { redirect_to @board, notice: 'Board was successfully created.' }
      format.json { render :show, status: :created, location: @board }
    else
      format.html { render :new }
      format.json { render json: @board.errors, status: :unprocessable_entity }
    end
  end
end

但我还想将role 设置为admin.. 在我将记录保存在board#create 操作中之后,我能想到的唯一方法是current_user.user_boards.find_by(board_id:@board).update_attribute(:role, 'admin')。这个查询有点让我想呕吐,我不敢相信没有更好的方法。

我的board_params 非常简单&只允许基本标题:

def board_params
  params.require(:board).permit(:title)
end

编辑


我还想添加我用来提交board 的表单,以确保其中没有问题:

<div class="modal-body">
  <%= form_with(model: board, local: true) do |form| %>
    <% if board.errors.any? %>
      <div id="error_explanation">
        <h2><%= pluralize(board.errors.count, "error") %> prohibited this board from being saved:</h2>

        <ul>
          <% board.errors.full_messages.each do |message| %>
            <li><%= message %></li>
          <% end %>
        </ul>
      </div>
    <% end %>

    <div class="form-group">
      <%= form.label :title %><br/>
      <%= form.text_field :title, autofocus: true, class: 'form-control' %>
    </div>

    <div class="actions">
      <%= form.submit 'Create Board', class: 'btn btn-small btn-success btn-block' %>
    </div>
  <% end %>
</div>

非常感谢所有建议、提示和建设性的批评,因为我尝试使用 Ruby 语言和 Rails 框架来学习/熟悉自己。

【问题讨论】:

    标签: ruby-on-rails ruby activerecord relational-database jointable


    【解决方案1】:

    你没有显示board_params,但它可能看起来应该是这样的:

    def board_params
      params.require(:board).permit(:something, :whatever_else).merge!(role: :admin)
    end
    

    然后,执行以下操作:

    def create
      @board = current_user.boards.new(board_params)
    
      respond_to do |format|
        if @board.save
          format.html { redirect_to @board, notice: 'Board was successfully created.' }
          format.json { render :show, status: :created, location: @board }
        else
          format.html { render :new }
          format.json { render json: @board.errors, status: :unprocessable_entity }
        end
      end
    end
    

    这应该会创建一个正确设置user_idrole 的新板。

    顺便说一句,我个人更喜欢:

    class UserBoard < ApplicationRecord
      belongs_to :user
      belongs_to :board
      enum role: {
        admin:  0,
        member: 1
      }
    end
    

    所以我不必担心enum 在数组中的位置,正如docs 中所讨论的那样:

    请注意,当使用数组时,从值到数据库整数的隐式映射是根据值在数组中出现的顺序得出的。在示例中,:active 映射为 0,因为它是第一个元素,而 :archived 映射为 1。一般情况下,第 i 个元素映射到数据库中的 i-1。

    因此,一旦一个值被添加到枚举数组中,它在数组中的位置必须保持不变,新的值应该只添加到数组的末尾。要删除未使用的值,应使用显式哈希语法。

    【讨论】:

    • 我的board_params 正在运行:params.require(:board).permit(:title).merge!(role: :admin)。问题是它正在返回:ActiveModel::UnknownAttributeError (unknown attribute 'role' for Board.)。我怀疑这是因为role 属于user_boards 表而不是board 表。我遇到的另一个问题是current_user.boards.new(board_params) 没有创建与用户板的关联...
    • 所以事实证明,如果我要通过执行@board = current_user.boards.new(board_params) 创建板与用户的关联,我必须保存current_user 而不是@board.save,即奇特的&我不完全明白为什么。我开始想也许我的联想不正确?虽然看起来我正在关注所有的例子。
    【解决方案2】:

    所以我确实找到了一种非常简单且似乎是一种优雅的方式来处理用户 role 在创建板时自动完成。这是我的解决方案:

    class UserBoard < ApplicationRecord
      belongs_to :user
      belongs_to :board
      enum role: { admin: 0, member: 1 }
      before_create :set_admin
    
      private
    
      def set_admin
        self.role = 'admin'
      end
    end
    

    我只是添加了一个before_create 回调方法,它将在创建每条记录之前将连接表记录上的角色设置为admin。以后可以适当调整。

    我做了并且仍然可能对我的 board#create 方法有些困惑。我已经进行了一些研究并认为我可以解释为什么用于创建 user board 关联的 @jvillian 解决方案不起作用。所以这是提供的创建关联的方法:

      @board = current_user.boards.new(board_params)
        if @board.save
    

    但是,这里会发生Board 记录将被创建,您甚至可以在运行current_user.boards 时看到它是如何与用户“关联”的。但是不会创建连接表中的记录。我读到,在 has_many 关联的情况下,如果 child.new_record?返回 true (孩子尚未保存到 db),或者需要更新 foreign_key 列。因此:

    1. 将对象添加到已保存父级 (current_user.save) 上的关联会保存新子级。
    2. 将对象添加到未保存的父 (@board.save) 上的关联不会保存外键值(未创建连接表记录)
    3. 如果正在保存未保存的父对象并且在关联缓存中有一些子对象,则保存这些对象以更新 foreign_key。

    所以在这种情况下,current_user(用户对象)是父对象,并且缓存了 @board 的关联,然后更新 foreign_key 并创建连接表记录。另一方面,保存@board 将保存一个新的Board 对象并更新分配给current_user.boards 的缓存Board 对象,但不会创建foreign_key。

    我希望这是有道理的,更重要的是它是正确的。否则请告诉我。我想确保我有一个正确的理解。

    【讨论】:

      猜你喜欢
      • 2011-01-21
      • 1970-01-01
      • 1970-01-01
      • 2012-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多