I have a Profile model and it has many attributes like email, image, age, address etc. A end user can make some attributes private so that other users are not able to view that. I solved this issue by adding a column to table private_attr
and serialized it to store a hash like:-
我有一个Profile模型,它有许多属性,如电子邮件,图像,年龄,地址等。最终用户可以将某些属性设为私有,以便其他用户无法查看。我通过向表private_attr添加一个列并将其序列化以存储类似的哈希来解决此问题: -
{email: true, address: true, age: false }
Here attributes as key having value true
are considered as private and not shown to the user other than to whom these belongs.
这里作为具有值true的键的属性被认为是私有的,并且除了这些属于谁之外不向用户显示。
I want to know is this the best way to solve this problem, or there is any other way. Thanks in advance.
我想知道这是解决这个问题的最佳方法,还是有其他办法。提前致谢。
4 个解决方案
#1
6
I think you can serialize just the fields that the user want to be private in an array(instead of a hash). Like [:email, :address]
(using your example)
我认为你可以序列化用户想要在数组中私有的字段(而不是哈希)。喜欢[:email,:address](使用你的例子)
Then, when you will render the view, just check if the field is present in that column. Something like
然后,当您渲染视图时,只需检查该列中是否存在该字段。就像是
<%= user.email unless user.private_fields.include?(:email) %>
You can even extract that logic to a view helper to avoid duplication.
您甚至可以将该逻辑提取到视图助手以避免重复。
<%= show_if_allowed(user, :email) %>
Then create a helper like this
然后创建一个这样的帮助器
def show_if_allowed(user, field)
user[field] unless user.private_fields.include?(field)
end
#2
3
Actually, that's what I would do if I had to deal with 8 to 10 attributes. But, if you have way too many attributes for your Profile
model class and have complex logic of showing these attributes on the basis of user's group, public, shared, etc. then I'd encourage you to move this to either a separate model class, let's say: "ProfileConfiguration
or ProfileSetting
", which will maintain each attribute on a row level, or you can move these settings to Redis, where the structure will be like: user_id: {attribute_name: true, type: 'type_name'}
but then there's a drawback that you'll be depending on Redis server's availability.
实际上,如果我必须处理8到10个属性,那就是我要做的。但是,如果您的Profile模型类有太多属性,并且具有基于用户组,公共,共享等显示这些属性的复杂逻辑,那么我建议您将其移动到单独的模型类中,让我们说:“ProfileConfiguration或ProfileSetting”,它将在行级别维护每个属性,或者您可以将这些设置移动到Redis,其结构如下:user_id:{attribute_name:true,type:'type_name'}但是然后有一个缺点,你将依赖Redis服务器的可用性。
Now, in your case:
现在,在你的情况下:
serialize :profile_preferences, Hash
and then you maintain it with(as you mentioned, just in the opposite manner for their assignment):
然后你保持它(正如你所提到的,只是以相反的方式进行分配):
{email: false, address: false, age: true }
However, you can go ahead and create some handy methods you can call on your profile object:
但是,您可以继续创建一些可以在配置文件对象上调用的方便方法:
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
Now, you get handy methods like: show_email?
, show_address?
, and show_age?
on Profile
class's object which you can delegate to User
class instance. So, you can now do something like this in your views, for example:
现在,你得到了方便的方法,如:show_email?,show_address?和show_age?在Profile类的对象上,您可以将该对象委托给User类实例。所以,你现在可以在你的视图中做这样的事情,例如:
<%= "Email: {user.email}" if user.show_email? %>
<%= "Address: {user.address}" if user.show_address? %>
<%= "Age: {user.age}" if user.show_age? %>
#3
2
You asked if there were other ways. Sure. Whether one is better is up to you.
你问是否有其他方法。当然。一个人是否更好取决于你。
Lots of Columns
很多专栏
You could create additional columns for each attribute. email_protected: true/false (etc). If you only have 3 columns, that's not too bad but wouldn't scale well if you are protecting lots of attributes.
您可以为每个属性创建其他列。 email_protected:true / false(etc)。如果你只有3列,那不是太糟糕但如果保护很多属性就不能很好地扩展。
Many to Many relationship
多对多关系
You could have an additional model, such as ProtectedAttribute. A Profile has_many ProtectedAttributes and store the data in separate tables instead of serializing. This eliminates the serialized data into a single column.
您可以拥有其他模型,例如ProtectedAttribute。配置文件has_many ProtectedAttributes并将数据存储在单独的表中,而不是序列化。这将序列化数据排除在单个列中。
Additional Details follow as requested
其他详细信息如下所示
# 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
The migrations (you'll need to tweak if you stick with Profile):
迁移(如果你坚持使用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
#4
1
You could use a gem named cancan to handle the rights your different kind of users you define in a ruby file (ability.rb
). Then, I suggest you to have may be different serializer for one resource, each serializer dedicated to a specific user role, it's easy to do with active-model-serializer.
您可以使用名为cancan的gem来处理您在ruby文件中定义的不同类型用户的权限(ability.rb)。然后,我建议您可能有一个资源的不同序列化程序,每个序列化程序专用于特定的用户角色,使用active-model-serializer很容易。
#1
6
I think you can serialize just the fields that the user want to be private in an array(instead of a hash). Like [:email, :address]
(using your example)
我认为你可以序列化用户想要在数组中私有的字段(而不是哈希)。喜欢[:email,:address](使用你的例子)
Then, when you will render the view, just check if the field is present in that column. Something like
然后,当您渲染视图时,只需检查该列中是否存在该字段。就像是
<%= user.email unless user.private_fields.include?(:email) %>
You can even extract that logic to a view helper to avoid duplication.
您甚至可以将该逻辑提取到视图助手以避免重复。
<%= show_if_allowed(user, :email) %>
Then create a helper like this
然后创建一个这样的帮助器
def show_if_allowed(user, field)
user[field] unless user.private_fields.include?(field)
end
#2
3
Actually, that's what I would do if I had to deal with 8 to 10 attributes. But, if you have way too many attributes for your Profile
model class and have complex logic of showing these attributes on the basis of user's group, public, shared, etc. then I'd encourage you to move this to either a separate model class, let's say: "ProfileConfiguration
or ProfileSetting
", which will maintain each attribute on a row level, or you can move these settings to Redis, where the structure will be like: user_id: {attribute_name: true, type: 'type_name'}
but then there's a drawback that you'll be depending on Redis server's availability.
实际上,如果我必须处理8到10个属性,那就是我要做的。但是,如果您的Profile模型类有太多属性,并且具有基于用户组,公共,共享等显示这些属性的复杂逻辑,那么我建议您将其移动到单独的模型类中,让我们说:“ProfileConfiguration或ProfileSetting”,它将在行级别维护每个属性,或者您可以将这些设置移动到Redis,其结构如下:user_id:{attribute_name:true,type:'type_name'}但是然后有一个缺点,你将依赖Redis服务器的可用性。
Now, in your case:
现在,在你的情况下:
serialize :profile_preferences, Hash
and then you maintain it with(as you mentioned, just in the opposite manner for their assignment):
然后你保持它(正如你所提到的,只是以相反的方式进行分配):
{email: false, address: false, age: true }
However, you can go ahead and create some handy methods you can call on your profile object:
但是,您可以继续创建一些可以在配置文件对象上调用的方便方法:
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
Now, you get handy methods like: show_email?
, show_address?
, and show_age?
on Profile
class's object which you can delegate to User
class instance. So, you can now do something like this in your views, for example:
现在,你得到了方便的方法,如:show_email?,show_address?和show_age?在Profile类的对象上,您可以将该对象委托给User类实例。所以,你现在可以在你的视图中做这样的事情,例如:
<%= "Email: {user.email}" if user.show_email? %>
<%= "Address: {user.address}" if user.show_address? %>
<%= "Age: {user.age}" if user.show_age? %>
#3
2
You asked if there were other ways. Sure. Whether one is better is up to you.
你问是否有其他方法。当然。一个人是否更好取决于你。
Lots of Columns
很多专栏
You could create additional columns for each attribute. email_protected: true/false (etc). If you only have 3 columns, that's not too bad but wouldn't scale well if you are protecting lots of attributes.
您可以为每个属性创建其他列。 email_protected:true / false(etc)。如果你只有3列,那不是太糟糕但如果保护很多属性就不能很好地扩展。
Many to Many relationship
多对多关系
You could have an additional model, such as ProtectedAttribute. A Profile has_many ProtectedAttributes and store the data in separate tables instead of serializing. This eliminates the serialized data into a single column.
您可以拥有其他模型,例如ProtectedAttribute。配置文件has_many ProtectedAttributes并将数据存储在单独的表中,而不是序列化。这将序列化数据排除在单个列中。
Additional Details follow as requested
其他详细信息如下所示
# 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
The migrations (you'll need to tweak if you stick with Profile):
迁移(如果你坚持使用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
#4
1
You could use a gem named cancan to handle the rights your different kind of users you define in a ruby file (ability.rb
). Then, I suggest you to have may be different serializer for one resource, each serializer dedicated to a specific user role, it's easy to do with active-model-serializer.
您可以使用名为cancan的gem来处理您在ruby文件中定义的不同类型用户的权限(ability.rb)。然后,我建议您可能有一个资源的不同序列化程序,每个序列化程序专用于特定的用户角色,使用active-model-serializer很容易。