【问题标题】:Conditional model validations based on a role基于角色的条件模型验证
【发布时间】:2017-08-01 16:55:05
【问题描述】:

我在用户模型中使用枚举实现了用户角色:

enum role: [:staff, :clinician]

我有一个带有 User belongs_to :university 的大学模型和一个带有 has_many :users 的大学模型。

我的应用程序的工作方式是“员工”将属于一所大学,但“临床医生”是私人执业,因此不需要属于一所大学,也不需要在注册时选择一个。

如果用户选择临床医生,我的注册表单设置为隐藏大学字段,但我想确保我的验证设置为要求任何在注册时选择员工的用户也必须选择一所大学,并且任何在注册时选择临床医生的用户如果选择一所大学,则验证失败。

这是用户注册表单的角色部分:

<%= f.label :role %>
      <%= f.select(:role, User.roles.keys.map {|role| [role.titleize,role]}, :include_blank => "Please Select", id: 'user_role')  %>

      <%= content_tag :div, class: 'js-dependent-fields', data: { 'select-id': 'user_role', 'option-value': 'staff'} do %>
      <%= f.label :university_id%>
      <%= collection_select( :user, :university_id, University.all, :id, :name, prompt: true) %>

【问题讨论】:

    标签: ruby-on-rails


    【解决方案1】:

    它需要更多的额外设置,但我认为随着时间的推移灵活性会有所回报:

    尝试将单表继承与您的枚举角色相结合。您将能够更轻松地为您的不同角色定义单独的回调、验证、范围和关联,同时继承您希望它们共同共享的那些。例如,您可以只设置Staff belongs_to :university,而Clinician 不设置。

    # Stripped down schema
    
    create_table "universities", force: :cascade do |t|
      t.datetime "created_at", null: false
      t.datetime "updated_at", null: false
    end
    
    create_table "users", force: :cascade do |t|
      t.datetime "created_at", null: false
      t.datetime "updated_at", null: false
      t.bigint "university_id"
      t.integer "role"
      t.index ["university_id"], name: "index_users_on_university_id"
    end
    
    # Models
    
    class University < ApplicationRecord
      has_many :staffs
    end
    
    class User < ApplicationRecord
      self.inheritance_column = :role
      enum role: { Staff: 0, Clinician: 1 }
    end
    
    class Clinician < User
    end
    
    class Staff < User
      belongs_to :university
    end
    
    Staff.first.university # => returns instance of University
    Clinician.first.university # => raises NoMethodError
    University.first.staffs # => returns collection of Staff objects
    University.first.clinicians # => raises NoMethodError
    

    请注意,没有type 列。它已被用于enumrole:integer 列通过设置self.inheritance_column = :role 覆盖。您可以使用字符串/符号表示("Staff"Staff.newUser.first.Staff?User.first.Staff!User.new(role: "Staff")enum 角色进行交互,并且 ActiveRecord 负责将该字符串转换为数据库的正确整数查询。

    例如,这是User.where(role: "Staff")的查询

    SELECT  "users".* FROM "users" WHERE "users"."role" = 0
    

    Staff.all 返回相同的结果,但查询的措辞略有不同

    SELECT  "users".* FROM "users" WHERE "users"."role" IN (0)
    

    查看这个问题了解更多详情:Same Model with different columns Rails

    【讨论】:

    • 感谢您提出这个问题。我一直在研究单表继承,想知道它如何与枚举一起使用。
    • 我已尝试根据该问题实施 STI,但似乎无法识别方法...我正在考虑仅取消枚举并使用帖子中描述的类型列。你知道有什么缺点吗?有什么理由保留枚举?
    • 如果您要合并它们,您需要确保覆盖 STI 的默认 type 列。将self.inheritance_table = :role 添加到User 模型应该可以做到。枚举的优点是查找整数的速度要快得多,尤其是在有很多记录的情况下,并且它们免费为您提供了一些方便的方法。 @mike9182
    • 谢谢 Simon - 您对我在用户注册表单中收集的内容有什么建议吗? (枚举角色或 STI 类型?)我可以让我在注册时收集的任何一个设置另一个,而不必要求用户使用单独的表单字段定义他们的角色和他们的类型?
    • @mike9182 您应该只使用这些列之一,而不是两者。如果您正在组合 STI 和 enum 并使用 role 作为您的 enum 列,那么您不应该有 type 列:enum 角色与您的 STI 类型相同。您与字符串版本进行交互,Rails 将数据库转换为 role 整数。我用更多的代码示例更新了答案,希望能证明这一点。如果不清楚,请告诉我
    【解决方案2】:

    您可以在 User.rb 模型中为 validates 调用提供条件:

    validates :university, presence: true, if: lambda { self.role.to_s == 'staff' }
    # watch out for symbol vs. string in your self.role array
    

    而且我认为(从未做过,但我想这会起作用)您可以为 :clinician 角色执行此操作:

    validates :university, presence: false, if: lambda { self.role.to_s == 'clinician' }
    

    【讨论】:

    • 感谢您的回复!这给了我以下 NoMethodError: undefined method `include?'对于 nil:NilClass
    • 这意味着self.role 不是一个数组,因此(还)没有填充分配的角色。您可以在 if 的 lambda 中执行一个简单的 raise self.inspect 来查看您可以从这里访问的内容。另外,你可能想要一个validates :role, presence: true 之前 我建议的validates,好像你没有选择/设置角色,validates :role 将失败
    • 您是对的,由于某种原因,注册表单没有正确传递或保存角色。你能说出为什么吗?我将表单添加到原始问题中。
    • 使用enumrole 应该是'staff''clinician',它不会是Array。它应该是validates :university, presence: true, if: lambda { self.role == 'staff' }。而且我认为presence: false 不会起作用。这意味着您告诉验证不要进行presence 检查
    • @m.simonborg 是的,我不确定presence: false... 我得看看validates 的实现。此外,为枚举 +1
    猜你喜欢
    • 2018-03-09
    • 2016-01-03
    • 1970-01-01
    • 1970-01-01
    • 2021-11-22
    • 2019-10-10
    • 2020-09-23
    • 2017-08-03
    • 1970-01-01
    相关资源
    最近更新 更多