【问题标题】:What is the best way to set some attributes of Profile model is public(visible) or private(invisible) to other user?设置 Profile 模型的某些属性的最佳方法是对其他用户公开(可见)还是私有(不可见)?
【发布时间】:2014-12-01 22:12:56
【问题描述】:

我有一个 Profile 模型,它具有许多属性,例如电子邮件、图像、年龄、地址等。 最终用户可以将某些属性设为私有,以便其他用户无法查看。 我通过在表private_attr 中添加一列解决了这个问题,并将其序列化以存储如下哈希:-

{email: true, address: true, age: false }

这里的属性作为具有值true 的键被认为是私有的,不会向除这些属性所属的用户以外的用户显示。

我想知道这是解决此问题的最佳方法,还是有其他方法。 提前致谢。

【问题讨论】:

    标签: ruby-on-rails ruby activerecord serialization


    【解决方案1】:

    您问是否有其他方法。当然。哪个更好取决于您。

    很多列

    您可以为每个属性创建额外的列。 email_protected:真/假(等)。如果您只有 3 列,那还不错,但如果您要保护大量属性,则无法很好地扩展。

    多对多关系

    您可以有一个额外的模型,例如 ProtectedAttribute。 Profile has_many ProtectedAttributes 并将数据存储在单独的表中,而不是序列化。这将序列化的数据消除到单个列中。

    根据要求提供其他详细信息

    # I'm using User instead of Profile as Profile is a protected word in Rails
    class User < ActiveRecord::Base
      has_many :profile_protected_attributes
      has_many :protected_attributes, through: :profile_protected_attributes
    
      def protect_attribute?(name)
        protected_attributes.where(name: name).present?
      end
    
      def show_attribute?(name)
        !protect_attribute(name)
      end
    
      def protect_attribute(name)
        return if self.protect_attribute?(name)
        protected_attributes << ProtectedAttribute.find_by_name(name)
      end
    
      def unprotect_attribute(name)
        protected_attributes.delete(ProtectedAttribute.find_by_name(name))
      end
    end
    
    class ProtectedAttribute < ActiveRecord::Base
      has_many :profile_protected_attributes
      has_many :users, through: :profile_protected_attributes
    end
    
    # The join model
    class ProfileProtectedAttribute < ActiveRecord::Base
      belongs_to :user
      belongs_to :protected_attribute
    end
    

    迁移(如果您坚持使用 Profile,则需要进行调整):

    class CreateProtectedAttributes < ActiveRecord::Migration
      def change
        create_table :protected_attributes do |t|
          t.string :name
          t.timestamps
        end
      end
    end
    
    class CreateProfileProtectedAttributes < ActiveRecord::Migration
      def change
        create_table :profile_protected_attributes do |t|
          t.integer :user_id
          t.integer :protected_attribute_id
          t.timestamps
        end
      end
    end
    

    【讨论】:

    • 你能解释一下ProtectedAttribute模型的可能解决方案吗?
    • 当然。当我有时间写详细信息时,我会更新我的答案。
    • 我已经用类关系和一些示例辅助方法更新了我的答案。
    【解决方案2】:

    您可以使用名为 cancan 的 gem 来处理您在 ruby​​ 文件 (ability.rb) 中定义的不同类型用户的权限。然后,我建议您为一个资源使用不同的序列化程序,每个序列化程序专用于特定的用户角色,active-model-serializer 很容易做到。

    【讨论】:

      【解决方案3】:

      我认为您可以仅序列化用户希望在数组中私有的字段(而不是哈希)。喜欢[:email, :address](使用您的示例)

      然后,当您渲染视图时,只需检查该字段是否存在于该列中。类似的东西

      <%= user.email unless user.private_fields.include?(:email) %>
      

      您甚至可以将该逻辑提取到视图助手以避免重复。

      <%= show_if_allowed(user, :email) %>
      

      然后像这样创建一个助手

      def show_if_allowed(user, field)
          user[field] unless user.private_fields.include?(field)
      end
      

      【讨论】:

      • 结合演示者模式(在视图上实例化的类,具有关于如何显示属性的方法等),这将是 - 在我看来 - 最好的方式。 +1
      • @MrYoshiji 那么也请添加你的答案。
      • @SachinSingh 不是将 show_if_allowed 方法放在帮助器中,而是将它放在演示器中,就像这样 blog.steveklabnik.com/posts/2011-09-09-better-ruby-presenters
      【解决方案4】:

      实际上,如果我必须处理 8 到 10 个属性,我会这样做。但是,如果您的 Profile 模型类的属性太多,并且具有基于用户组、公共、共享等显示这些属性的复杂逻辑,那么我鼓励您将其移至单独的模型类,比方说:“ProfileConfigurationProfileSetting”,它将在行级别上维护每个属性,或者您可以将这些设置移动到 Redis,其中的结构将类似于:user_id: {attribute_name: true, type: 'type_name'} 但有一个缺点您将取决于 Redis 服务器的可用性。

      现在,在你的情况下:

      serialize :profile_preferences, Hash
      

      然后你用它来维护它(正如你提到的,只是以与他们的任务相反的方式):

      {email: false, address: false, age: true }
      

      但是,您可以继续创建一些方便的方法来调用您的配置文件对象:

      after_initialize :load_profile_preferences
      
      private
      
      def load_profile_preferences
        profile_preferences.each do |attr, value|
          self.class.send(:define_method, "show_#{attr.to_s}?") { value }
        end
      end
      

      现在,您可以在 Profile 类的对象上获得方便的方法,例如:show_email?show_address?show_age?,您可以通过 delegateUser 类实例。因此,您现在可以在视图中执行类似的操作,例如:

      <%= "Email: {user.email}" if user.show_email? %>
      <%= "Address: {user.address}" if user.show_address? %>
      <%= "Age: {user.age}" if user.show_age? %>
      

      【讨论】:

      • 谢谢,我有兴趣了解更多关于第一种方法的信息,即使用ProfileConfiguration 模型,您能否详细说明一下,如何使用它管理多个用户属性。
      • 好的,我很乐意把它说得详细一点,请允许我一点时间。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-02
      • 1970-01-01
      • 1970-01-01
      • 2012-06-04
      • 2013-06-13
      • 1970-01-01
      相关资源
      最近更新 更多